首先声明, 这篇文章是想说明一下最新版本的 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