TypeScript 實踐

做者簡介:aoto 螞蟻金服·數據體驗技術團隊javascript

摘要:以前寫過一篇《TypeScript 體系調研報告》,通過半年多的螞蟻金服數據平臺大規模 JS 項目實戰,沉澱了一些編程實戰經驗和感悟。java

前言

TypeScript 是有類型定義的 JS 的超集,包括 ES五、ES5+ 和其餘一些諸如泛型、類型定義、命名空間等特徵的集合,爲了大規模 JS 應用而生。對於 TypeScript 自己,更多信息請參考《TypeScript 體系調研報告》。本文只記錄 TypeScript 在咱們實際項目中產生的一些實際有用的價值。咱們的項目基於 React 體系,所以本文着重關注 React 體系和 TypeScript 結合使用的經驗。react

React in TypeScript

在 TypeScript 開發環境下寫 React 組件,與 ES6 的區別主要就是 Props 和 State 的定義。若是是 ES6,大概是這樣:git

import PropTypes from 'prop-types';
class App extends React.PureComponent {
  state = {
    aState: '',
    bState: '',
  };
}

App.propTypes = {
  aProps: PropTypes.string.isRequired,
  bProps: PropTypes.string.isRequired,
};
複製代碼

若是用 TypeScript 來寫,大概是這樣(本文的 interface 定義默認以 I 開頭):github

interface IProps {
  aProps: string;
  bProps: string;
}
interface IState {
  aState: string;
  bState: string;
}

class App extends React.PureComponent<IProps, IState> {
  state = {
    aState: '',
    bState: '',
  };
}
複製代碼

TypeScript 自帶 JSX 解析器,所以爲了充分利用它自己的是靜態檢查功能,因此用泛型來定義 Props 和 State 的類型。如上定義後,在成員方法中經過this.props.this.state.使用 Props 和 State 時能夠智能提示,並且會作類型檢查。看起來寫沒有變的簡潔多少,但 TypeScript 來開發 React 應用有一個很大的優點,TS 環境中的 React 組件屬性是靜態的,也就是說能夠作靜態檢查,而且在支持 TS 的 IDE 下寫的過程當中能夠自動自動提示(是否存在以及類型是否正確)/補全,**這對於保障大型 React 應用的代碼質量和運行時質量頗有幫助。**而 ES6 環境中的 prop-types 屬性是動態的,也就是運行期作檢查,也不能作自動提示/補全。由此看來除了 JS 代碼自己, TypeScript 對 React 組件開發也是頗有好處的。typescript

Redux 是最流行的狀態管理庫,通常和 React 結合使用。在 TypeScript 和 ES6 環境下,Redux 代碼寫法區別不大,有個問題就是,React 和 Redux 的綁定函數 connect 需用函數寫法,而不建議用裝飾器寫法。由於 TS 對 類裝飾器的靜態解析還不支持,用了裝飾器寫 connect 就不能利用 TS 的靜態解析的好處了。編程

const mapStateToProps = (state: {}, ownProps: IProps) => {};
const mapDispatchToProps = {};

export default connect(mapStateToProps, mapDispatchToProps)(App);
複製代碼

Antd 是一個流行的 React 組件庫,提供了 TS 類型聲明,在寫組件的時候能夠進行屬性的自動提示和檢查,不肯定的能夠進入類型聲明文件查看,減小了查閱在線 API 文檔的次數和屬性拼寫錯誤的可能性。框架

React + Redux + TypeScript 的編程體驗:編程語言

react+typescript.gif | center | 780x624

面向對象設計

複雜軟件須要用複雜的設計,面向對象就是很好的一種設計方式,使用 TS 的一大好處就是 TS 提供了業界承認的類( ES5+ 也支持)、泛型、封裝、接口面向對象設計能力,以提高 JS 的面向對象設計能力。函數

泛型

泛型,簡單說就是泛化的類型。咱們項目中的泛型實踐主要在 React 組件中,這也是借鑑了 React 的 Component 和 PureComponent 在 TS 中的定義。代碼以下:

class Component<P, S> {}
class PureComponent<P = {}, S = {}> extends Component<P, S> {}
複製代碼

P 和 S 就是泛型,這裏的 P 是 Props,S 是 State。從這裏能夠看到若是咱們要基於 React 組件進行基於繼承的設計,那泛型就能夠發揮做用了。這裏舉一個例子,假如咱們要設計一個複雜 UI 模塊,有兩層繼承,BaseComponent 繼承 React.PureComponent,XComponent 繼承 BaseComponent,效果就是 XComponent extends BaseComponent extends React.PureComponent,示例代碼以下:

export interface IBaseProps {}

export interface IXProps {}

export class BaseComponent<
  IProps extends IBaseProps,
  IState = {}
> extends React.PureComponent<IProps, IState> {}

export class XComponent<
  IProps extends IXProps,
  IState = {}
> extends BaseComponent<IProps, IState> {}
複製代碼

BaseComponent 的屬性(props)定義是 IProps,繼承自 IBaseProps,這樣 BaseComponent 組件中就可使用 IBaseProps 中定義的屬性了。

XComponent 的屬性定義是 IProps,繼承自 IXProps,同時 XComponent 又繼承自 BaseComponent,由於咱們在 BaseComponent 類的屬性泛型定義中 IProps 是繼承 IBaseProps,這樣兩個條件共同做用於 XComponent,就有了 IProps extends IXProps extends IBaseProps 的效果了,就能夠在 XComponent 中同時使用 IBaseProps 和 IXProps 的屬性了。

封裝

咱們都知道,封裝在對於面向對象軟件設計很是有用,而封裝各個模塊的實現細節,就能夠在有效管理軟件的複雜度。 TS 對於代碼封裝性的幫助主要體如今它提供了相似於 Java 的訪問控制符。有了 private/protected/public,咱們能夠自主的控制類須要對外暴露的接口。訪問權限需儘量嚴,也就是說無需對外暴露的用 private 或者 protected ,如無需被子類使用的就用 private,只有明確須要對外暴露的接口採用 public 來描述。在一些非 React 組件的公共類,封裝特性尤其必要。

接口

接口在面向對象設計裏面也是很重要的,接口也就是對外提供服務的端口。咱們舉一個例子,一個公共模塊類 A ,提供了幾個接口findByIdupdateDatadestroy。那麼咱們要定義一個接口的定義,方便別的模塊使用(這和傳統編程語言的接口有點不一樣,TS 的接口並不會本身運行):

export interface IClassA {
  findById(id: string): IModel;
  updateData(data: IModel): void;
  destroy(): void;
}
複製代碼

調用方:

const a = new IClassA();
a.findById('1');
a.updateData({ id: '1', name: 'a' });
a.destroy();
複製代碼

有了接口聲明,咱們在使用該模塊的時候能夠清晰的看到它到底有哪些接口,方法的入參是什麼,返回值是什麼。同時也有代碼的自動提示,提高開發效率,減小拼寫錯誤致使的低級 Bug。

代碼質量

靜態檢查

如今流行的庫如 React 、Redux 、 Lodash 、 Antd 等等都有 TS 類型聲明,加上咱們本身業務代碼完善的類型定義,整個代碼庫的健康度能夠較好的保持住。這在傳統的大規模 JS 應用裏面是很可貴的。之前總感受 JS 一複雜,就感受質量難以保證,線上運行也有點虛,說不定何時就爆出一個 's' is undefined, 'b' is not a function 之類的錯誤。如今有了靜態檢查,內心更有底了

加強設計階段

上面章節已經就 TS 對面向對象設計能力的加強作了描述。設計的加強,是能夠提高代碼質量的。良好的設計面對需求迭代的不斷衝擊,能夠保持代碼的可維護性和可擴展性,也就提高了代碼的質量和健康度。

總結

如今的 Web 應用不少都是複雜的單頁應用,尤爲是一些工具類的產品,複雜度慢慢接近甚至不亞於一些傳統的桌面軟件,如 Word、Excel、SQL 客戶端、數據集成分析工具等等,這些 Web 應用均可以說是大規模 JS 應用,須要一個團隊來協做迭代開發。那麼協做效率、代碼可讀性、可維護性、健壯性等等都是咱們應該重點專一的點。

協做效率自己比較難以衡量,但能夠從閱讀、維護別人代碼的難度和效率這一點來講明。若是有有完善的類型定義,再加上智能的 TS IDE(如 VS Code),從咱們項目的實踐經驗來看能夠顯著的提高代碼的可讀性、可維護性。不少時候都是看看源代碼就能較容易的發現一些問題和 Bug。隨着產品的不斷迭代,一來一回之間就提高了整個團隊的協做效率。

咱們項目的 TS 校驗規則是很嚴格的,意味着代碼中基本沒有 any ,都須要定義具體類型。對於一些公共的模塊,咱們也會嚴格定義 private、protected、public 等訪問控制符,這意味着代碼即文檔,代碼就能說明哪些是對外的接口,哪些是內部使用的。

一些經常使用的第三方庫和框架都有完善的 TS 類型定義,同時整個項目業務代碼都有完善的 TS 類型定義,靜態檢查出錯的都會在 IDE 中提示出來,甚至能夠阻斷構建流程,這樣能夠減小 Bug 的產生,代碼質量更爲可控,代碼健康度更高,內心也更有底了。

相關資源

對咱們團隊感興趣的能夠關注專欄或者發送簡歷至'tao.qit####alibaba-inc.com'.replace('####', '@'),歡迎有志之士加入~

原文地址:github.com/ProtoTeam/b…

相關文章
相關標籤/搜索