目前项目中对于 typescript 的应用停留在比较基础的层面,并没有充分使用它的功能。通过对 ts 更深入的学习和使用,能避免一些潜在的问题。
实践 Tips
尽量不使用 any,在不能确定类型的情况下,用 unknown 会更合适
any
任何类型,会忽略语法检查unknown
不可预知的类型,不会忽略语法检查
公共的用 interface 实现,不能用 interface 实现的再用 type 实现,即用 interface 描述数据结构,用 type 描述类型关系;
interface
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
type
类型别名,给类型取一个别名,并不会产生新的类型
type 和 interface 的异同
都可以用来描述一个对象或者函数
都可以拓展,并且两者不是相互独立的,但语法不通。也就是说可以
interface extends type
,也可以type = type & interface
或者type=type | interface
多次声明一个 interface 会自动合并,但 type 不会,type 唯一且不能重复
type 只能用于声明右侧,interface 可以用于任何地方
type 可以声明基本类型、联合类型
A | B
、交叉类型A & B
、元组,interface 只能声明对象和函数
1 | interface IA { |
泛型
软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像 C#和 Java 这样的语言中,可以使用泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
泛型就像是类型的参数,和函数一样,可以有默认值。除此之外,还可以用 extends 对参数本身需要满足的条件进行限制。
在定义一个函数、type、interface、class 时,在名称后面加上<>表示即接受类型参数。而在实际调用时,不一定需要手动传入类型参数,TS 往往能自行推断出来。在 TS 推断不准时,再手动传入参数来纠正。
泛型定义
泛型的应用场景:简单来讲就是在定义时无法确定类型,使用时才能确定类型
1 | interface InterResponse<T> { |
泛型可以用在普通类型定义、类定义、函数定义上
1 | // 普通类型定义 |
一个函数也可以传入多个泛型
1 | function testFunc2<T,U>(arg:T[],arg2:U):U{ |
泛型约束
限定范型 U 是范型 T 的子集
1 | function pick<T, U extends keyof T>() {} |
上图中这种情况,编辑器中提示类型 T 上没有 age 和 name,可以通过 extends 改造一下
1 | interface Person<J, K> { |
泛型条件
这里不限制 T 一定要是 U 的子类型,如果是 U 子类型,则将 T 定义为 X 类型,否则定义为 Y 类型
1 | T extends U? X: Y |
泛型推断 infer
1 | type ParamType<T> = T extends (arg: infer P) => any ? P : T |
在这个条件语句 T extends (arg: infer P) => any ? P : T
中,infer P
表示待推断的函数参数。
整句表示为:如果 T
能赋值给 (arg: infer P) => any
,则结果是 (arg: infer P) => any
类型中的参数 P
,否则返回为 T
。
需要注意的是,infer 只能在條件類型的 extends 子句中使用,同時 infer 聲明的類型變量只在條件類型的 true 分支中可用。
常见泛型工具
Partial<T>
它用来将 T 中的所有的属性都变成可选项 ?
Record<K extends keyof any, T>
它用来生成一个属性为 K,类型为 T 的类型集合。
1 | type Foo = Record<'a', string> |
Pick<T, K extends keyof T>
将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
1 | interface Todo { |
Exclude<T, U>
它的作用是从 T
中排除掉所有包含的 U
属性。
1 | type TFoo = Exclude<1 | 2, 1 | 3> |
如果一个变量被指定为了TFoo
类型,它就只能被赋值为 2 了,否则就会报类型检查错误
type Extract<T, U>
它的作用正好和上面的Exclude
相反。而是从 T
中提取出所有包含的 U
属性值。
1 | type TFoo = Extract<1 | 2, 1 | 3> |
TFoo
类型最终只会包含 1。这是因为 T 包含 U 中的属性值 1,Extract 会将它提取出来生成一个类型
type Omit<T,K>
用来忽略 T 中的 K 属性
1 | type User = { |
type NonNullable<T>
它的作用是去除 T 中包含的 null 或者 undefined。