前言
TypeScript 是 JS 类型的超集,并支持了泛型、类型、命名空间、枚举等特性,弥补了 JS 在大型应用开发中的不足。
使用 TypeScript 的编程体验最直观的是当在键盘上敲下 .
时,后面这一大串的提示真的是很方便,代码质量和效率提升十分明显,习惯后真的会使用不惯 JavaScript。
但是 TypeScript 和一些框架结合使用的话坑还是比较多的,例如使用 antd 框架的时候需要去查看框架提供的 .d.ts 的声明文件中一些复杂类型的定义,或者一些没有使用 TypeScript 插件结合起来使用会出现障碍等等。
React
在使用 React 时需要把 React 的 TypeScript 模块的声明包安装一下,否则会在使用 React 时无法找到某些模块而报错。
1 | npm i @types/react @types/react-dom -S |
使用 class 类名开发组件
如:
1 | import * as React from 'react' |
TypeScript 可以对 JSX 进行解析,充分利用其本身的静态检查功能,使用泛型进行 Props、 State 的类型定义。定义后在使用 this.state 和 this.props 时可以在编辑器中获得更好的智能提示,并且会对类型进行检查。
Component 的泛型如下:
1 | class Component<P, S> { |
Readonly 泛型把传入的值变为只读,如以上 props 定义的类型都变为只读。
Readonly 实现源码 node_modules/typescript/lib/lib.es5.d.ts ,是 typescript 自带的。
由于 props 属性被设置为只读,所以通过 this.props.size = ‘sm’ 进行更新时候 TS 检查器会进行错误提示
1 | Error:(23, 16) TS2540: Cannot assign to 'size' because it is a constant or a read-only property |
state 也一样为只读,而 React 的 state 更新需要使用 setState 方法,直接修改 state TS 检查器会进行错误提示。
使用 Function 开发组件
在 React 的声明文件中 已经定义了一个 SFC 类型,使用这个类型可以避免我们重复定义 children、 propTypes、 contextTypes、 defaultProps、displayName 的类型。
使用 SFC 进行无状态组件开发。
1 | import { SFC } from 'react' |
事件处理
我们在进行事件注册时经常会在事件处理函数中使用 event 事件对象,比如 input 输入后需要获取它的 value 值,那么就要使用event.target.value
,但没有定义 ts 就无法通过 ts 检查,而定义 any 会失去静态检查的意义,还有一个问题就是对 event 定义 any,那么获取 event.target 下的属性也会报错,因为 any 只对 event 的属性定义的,而自已通过 interface 对 event 对象进行类型声明编写的话又十分浪费时间,幸运的是 React 的声明文件提供了 Event 对象的类型声明。
如:
1 | class App extends React.Component { |
Event 事件对象类型
常用 Event 事件对象类型:
ClipboardEvent
剪贴板事件对象 DragEvent
拖拽事件对象 ChangeEvent
Change 事件对象 KeyboardEvent
键盘事件对象 MouseEvent
鼠标事件对象 TouchEvent
触摸事件对象 WheelEvent
滚轮事件对象 AnimationEvent
动画事件对象 TransitionEvent
过渡事件对象
实现源码的 node_modules/@types/react/index.d.ts
事件处理函数类型
当我们定义事件处理函数时有没有更方便定义其函数类型的方式呢?答案是使用 React 声明文件所提供的 EventHandler 类型别名,通过不同事件的 EventHandler 的类型别名来定义事件处理函数的类型。
EventHandler 类型实现源码 node_modules/@types/react/index.d.ts 。
1 | type EventHandler<E extends SyntheticEvent<any>> = { bivarianceHack(event: E): void }["bivarianceHack"]; |
EventHandler 接收 E ,其代表事件处理函数中 event 对象的类型。
bivarianceHack 为事件处理函数的类型定义,函数接收一个 event 对象,并且其类型为接收到的泛型变量 E 的类型, 返回值为 void。
1 | interface IProps { |
Promise 类型
在做异步操作时我们经常使用 async 函数,函数调用时会 return 一个 Promise 对象,可以使用 then 方法添加回调函数。
Promise<T>
是一个泛型类型,T 泛型变量用于确定使用 then 方法时接收的第一个回调函数的参数类型。
1 | interface IResponse<T> { |
我们首先声明 IResponse 的泛型接口用于定义 response 的类型,通过 T 泛型变量来确定 result 的类型。
然后声明了一个 异步函数 getResponse 并且将函数返回值的类型定义为 Promise<IResponse<number[]>>
。
最后调用 getResponse 方法会返回一个 promise 类型,通过 then 调用,此时 then 方法接收的第一个回调函数的参数 response 的类型为,{ message: string, result: number[], success: boolean}
。
Promise
工具与泛型的使用技巧
ts 提供的一些实用的泛型。
实现源码 node_modules/typescript/lib/lib.es5.d.ts
typeof
一般我们都是先定义类型,再去赋值使用,但是使用 typeof 我们可以把使用顺序倒过来。
1 | const options = { |
使用字面量类型限制值为固定的参数
如限制 props.pick 的值只可以是字符串 a、b、c ,不一定是字符串,当然也可以是数字 number。
1 | interface IProps { |
使用 Partial 将所有的 props 属性都变为可选值
1 | interface IProps { |
源码:
1 | type Partial<T> = { [P in keyof T]?: T[P] }; |
使用 Required 将所有 props 属性都设为必填项
效果与 Partial 相反
源码
1 | type Required<T> = { [P in keyof T]-?: T[P] }; |
条件类型
条件类型可以根据其他类型的特性做出类型的判断。
1 | T extends U ? X : Y |
如传入 T 与 U,当 T 中的内容在 U 中,那么就 true,返回 X,否则返回 U。
如:
1 | // 之前条件的判断方法 |
它是包含关系,如果包含怎么处理,不包含怎么处理,一般如下使用
1 | type aa = "a" | "b" | "c" | "d"; |
- Exclude<T,U>
从 T 中排除那些可以赋值给 U 的类型。
1 | type T = Exclude<1|2|3|4|5, 3|4> // T = 1|2|5 |
- Extract<T,U>
从 T 中提取那些可以赋值给 U 的类型。
1 | type T = Extract<1|2|3|4|5, 3|4> // T = 3|4 |
- Pick<T,K>
从 T 中取出一系列 K 的属性。
1 | interface Person { |
- Record<K,T>
将 K 中所有的属性的值转化为 T 类型。
1 | let person: Record<'name' | 'age', string> = { // {name: string;age: string;} |
- Omit<T,K>
从对象 T 中排除 key 是 K 的属性。
1 | interface Person { |
- NonNullable
排除 T 为 null 、undefined。
1 | type T = NonNullable<string | string[] | null | undefined>; // string | string[] |
- ReturnType
获取函数 T 返回值的类型。
1 | type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any; |
高阶组件
由于使用了高阶组件是把原来的组件包一层然后返回,但对于使用了 typescript 后传入到组件里的值经过包的那一层是没有组件的传入值的定义的,所以会报错,
那么我们就要对其进行处理。
这里我们使用泛型:P 表示传递到 HOC 的组件的 props。React.ComponentType
是 React.FunctionComponent
| React.ClassComponent
的别名,表示传递到 HOC 的组件可以是类组件或者是函数组件。
1 | import * as React from "react"; |