泛型编程(下)
本文介绍了一些 TS 内置工具类型的用法及实现
Readonly & Mutable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| type Person = { id: number; name: string; age: number };
type MyReadonly<T> = { readonly [K in keyof T]: T[K]; }; type X1 = MyReadonly<Person>;
type MyMutable<T> = { -readonly [K in keyof T]: T[K]; }; type X9 = MyMutable<Readonly<Person>>;
|
注意:
Mutable
并不是 TS 内置的函数
-readonly []
表示去掉属性前面的 Readonly
Partial & Required
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| type Person = { id: number; name: string; age: number };
type MyPartial<T> = { [K in keyof T]?: T[K]; }; type X2 = MyPartial<Person>;
type MyRequired<T> = { [K in keyof T]-?: T[K]; }; type X3 = MyRequired<Person>;
|
注意 -?
,表示去掉属性的可选,变为必选
1 2 3 4 5
| type MyExclude<A, B> = A extends B ? never : A; type X5 = Exclude<1 | 2 | 3, 1 | 2>;
type MyExtract<A, B> = A extends B ? A : never; type X6 = MyExtract<1 | 2 | 3, 2 | 4>;
|
按照之前讲的乘法分配律,有如下理解过程:
1 2 3 4 5 6 7 8
| type X5 = 1 | 2 | 3 extends 1 | 2 ? never : A
type X5 = | 1 extends 1 | 2 ? never : 1 | 2 extends 1 | 2 ? never : 2 | 3 extends 1 | 2 ? never : 3
type X5 = never | never | 3
|
X6
的理解过程同 X5
,这里不赘述
Omit & Pick
1 2 3 4 5 6 7 8 9 10 11 12 13
| type Person = { id: number; name: string; age: number };
type MyOmit<T, Key extends keyof T> = Pick<T, Exclude<keyof T, Key>>;
type MyOmit<T, Key extends keyof T> = { [K2 in keyof T as (K2 extends Key ? never : K2)]: T[K2]; }; type X7 = MyOmit<Person, 'name' | 'age'>;
type MyPick<T, Key extends keyof T> = { [K2 in Key]: T[K2]; }; type X8 = MyPick<Person, 'name' | 'age'>;
|
Key extends keyof T
是一个泛型约束,约束所传的 key
必须包含在 keyof T
中,即必须是 'id' | 'name' | 'age'
的子集
MyOmit
的第二个写法中,集合 K2 extends Key ? never : K2
小于或等于集合 K2 in keyof T
,依旧遵循之前讲的类型兼容,类型小的可赋值给类型大的,查看实现思路
Record
1 2 3 4 5
| type MyRecord<Key extends string | number | symbol, Value> = { [k in Key]: Value; };
type X4 = MyRecord<string, string>;
|
ReturnType
1 2 3 4 5 6 7 8 9 10 11
| type MyReturnType<T extends (...args: any[]) => unknown> = T extends (...args: any[]) => infer U ? U : never
const fn = (v: boolean) => { if (v) return 1 else return 2 }
type Result = MyReturnType<typeof fn>
|
Awaited
1 2 3 4 5 6 7
| type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer U> ? U extends PromiseLike<any> ? MyAwaited<U> : U : never;
type Result = MyAwaited<Promise<string>>
|
PromiseLike 表示一个对象具有 then 方法,它可以像 Promise 一样使用链式调用。
PromiseLike 可以表示内置的 Promise 对象和第三方库或自定义实现的 thenable 对象,因此 PromiseLike 提供了更广泛的兼容性。
- 限制传入的类型必须是
PromiseLike
- 使用
infer
推导 PromiseLike
参数类型
- 如果推导类型仍为
PromiseLike
,就循环调用 MyAwaited
- 否则直接返回推导类型
Parameters
1 2 3 4 5 6 7 8
| type MyParameters<T extends (...args: any[]) => unknown> = T extends (...args: infer P) => unknown ? P : never;
const foo = (arg1: string, arg2: number): void => {}
type Result = MyParameters<typeof foo>
|
- 限制传入的类型必须是函数类型
- 使用
infer
推导函数的参数类型
- 由于
...args
本身已经是元组类型,因此 infer P
最终推导出的,也是元组类型
其他
这里推荐一个用于练习 TS 类型体操的 repo:TypeScript 类型体操姿势合集
看看别人的体操: