首先聲明, 這篇文章是想說明一下最新版本的 TypeScript(3.0) 的新特性帶來的極大的 React 開發體驗提高. 而不是如何利用 TypeScript 開發 React 應用.react
這個特性就是對defaultProps
的支持, 在 TypeScript 的 Wiki 中有說明, 具體能夠參考這裏: Support for defaultProps
in JSX.git
在3.0版本以前, 咱們在開發 React 應用, 尤爲是在配合 redux 一類 HOC 庫的時候, 常常用到諸如 connect(TodoList)
, withRouter(TodoList)
之類的封裝. 而這些函數其實均可以用裝飾器
的方式來調用, 好比:github
export interface TodoListProps extends RouteComponentProps<{}> { todos: Todo[]; } @withRouter @connect(mapStateToProps) export class TodoList extends PureComponent<TodoListProps, {}> { render() { return null } }
可是在結合 TypeScript 的時候, 這中間有個問題, 就是裝飾器會自動注入一些 props 給組件, 這一部分屬性不須要外部傳入, 所以是可選的, 在strictNullCheck
屬性開啓的時候, 就會出現屬性衝突. 由於 TS 給不容許裝飾器修改被裝飾的對象的類型, 所以在 props 定義中爲required
屬性依然爲required
.redux
好比對於上面的例子, 在實例化TodoList
這個組件的時候, 必須要傳入全部的TodoListProps
所定義的屬性, 不然會有TS2322
錯誤.react-router
而在 TS 3.0 中, 能夠聲明defaultProps
屬性來代表某些屬性對外部組件而言是可選的. 好比:app
@withRouter @connect((state) => ({ todos: state.todos }) export class TodoList extends PureComponent<TodoListProps, {}> { static defaultProps: TodoListProps render() { return null } }
這裏的static defaultProps: TodoListProps
代表, 全部的TodoList
的 props TodoListProps
對外部組件都是可選的. 這就意味着外部組件能夠什麼屬性都不用傳也不會有錯誤. 同時內部而言全部的屬性都是NotNullable
.函數
綜上, 一般狀況下, 咱們的一個組件會有一部分屬性由裝飾器注入, 而另外一部分則須要外部實例化時傳入, 所以, 能夠將一個組件的 props 接口聲明成兩層結構, 第一層爲由裝飾器注入的部分, 第二層則爲完整的屬性接口, 而後將defaultProps
設置成爲第一層接口便可. 好比:ui
export interface TodoListInnerProps extends RouteComponentProps<{}> { todos: Todo[]; } export interface TodoListProps extends TodoListInnerProps { className?: string; onLoad?(): void; } @withRouter @connect((state) => ({ todos: state.todos }) export class TodoList extends PureComponent<TodoListProps, {}> { static defaultProps: TodoListInnerProps render() { return null } }
須要注意的是:spa
3.0.1
以上@types/react
要最新版withRouter
, connect
等函數在 @types
中, 簽名有問題, 須要手動修改一下:code
import { ComponentClass } from 'react' import { connect as nativeConnect, MapDispatchToPropsParam, MapStateToPropsParam } from 'react-redux' import { withRouter as nativeWithRouter } from 'react-router' export type ComponentDecorator<P = any> = <T extends ComponentClass<P>>(WrappedComponent: T) => T export const connect: <P, S = Todo>( mapState: MapStateToPropsParam<Partial<P>, P, S>, mapDispatch?: MapDispatchToPropsParam<Partial<P>, P> ) => ComponentDecorator = nativeConnect as any export const withRouter: ComponentDecorator = nativeWithRouter as any