- 类型系统其实在很多语言里面都是有的,比如 Java、Swift、C++等等,但是相对来说 TypeScript 的类型非常灵活。
- 这是因为 TypeScript 的目的是为 JavaScript 添加一套类型校验系统,因为 JavaScript 本身的灵活性,也让 TypeScript 类型系统 不得不增加更附加的功能以适配 JavaScript 的灵活性
- 所以 TypeScript 是一种可以支持类型编程的类型系统
- 这种类型编程系统为 TypeScript 增加了很大的灵活度,同时也增加了它的难度
- 如果你不仅仅在开发业务的时候为自己的 JavaScript 代码增加上类型约束,那么基本不需要太多的类型编程能力
- 但是如果你在开发一些框架、库,或者通用性的工具,为了考虑各种适配的情况,就需要使用类型编程
- TypeScript 本身为我们提供了类型工具,帮助我们辅助进行类型转换(前面有用过关于 this 的类型工具)
- 很多开发者为了进一步增强自己的 TypeScript 编程能力,还会专门去做一些类型体操的题目
条件类型
很多时候,日常开发中需要基于输入的值来决定输出的值,同样也需要基于输入的值的类型来决定输出的值的类型
条件类型(Conditional types)就是用来描述输入类型和输出类型之间的关系
- 条件类型的写法有点类似于 JavaScript 中的条件表达式(condition ? trueExpression : falseExpression ): 条件类型(Conditional Types)
SomeType extends OtherType ? TrueType : FalseType;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| type IDType = number | string;
type ResType = boolean extends IDType ? true : false;
function sum<T extends number | string>(num1: T, num2: T): T extends number ? number : string; function sum(num1: any, num2: any) { return num1 + num2; }
const res = sum(20, 30); const res2 = sum("abc", "cba");
|
条件类型中的推断
在条件类型中推断(Inferring Within Conditional Types)
- 条件类型提供了
infer
关键词,可以从正在比较的类型中推断类型,然后在 true
分支里引用该推断结果
比如现在有一个函数类型,想要获取到一个函数的参数类型和返回值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type CalcFnType = (num1: numebr, num2: number) => number
function foo() { return "abc" }
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never
type MyParameterType<T extends (...args; any[]) => any> = T extends (...args: infer P) => any ? P : never
type CalcReturnType = MyReturnType<CalcFnType> type FooReturnType = MyReturnType<typeof foo>
type CalcParameterType = MyParameterType<CalcFnType>
|
分发条件类型
当在泛型中使用条件类型的时候,如果传入一个联合类型,就会变成 分发的(distributive)
如果在 ToArray
传入一个联合类型,这个条件类型会被应用到联合类型的每个成员
- 当传入
string | number
时,会遍历联合类型中的每一个成员
- 相当于
ToArray | ToArray
- 所以最后的结果是:
string[] | number[]
1 2 3 4 5 6 7
| type toArray<T> = T extends any ? T[] : never;
type NumArray = toArray<number>;
type NumAndStrArray = toArray<number | string>;
|
内置工具
Partial
用于构造一个 Type 下面的所有属性都设置为可选的类型
格式:Partial<Type>
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface IKun { name: string; age: number; slogan?: string; }
type MyPartial<T> = { [P in keyof T]?: T[P]; };
type IKunOptional = MyPartial<IKun>;
|
Required
用于构造一个Type
下面的所有属性全都设置为必填的类型,这个工具类型跟 Partial
相反
格式:Required<Type>
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface IKun { name: string; age: number; slogan?: string; }
type MyRequired<T> = { [P in keyof T]-?: T[P]; };
type IKunRequire = MyRequired<IKun>;
|
Readonly
用于构造一个Type
下面的所有属性全都设置为只读的类型,意味着这个类型的所有的属性全都不可以重新赋值
格式:Readonly<Type>
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface IKun { name: string; age: number; slogan?: string; }
type MyReadonly<T> = { readonly [P in keyof T]: T[P]; };
type IKunReadonly = MyReadonly<IKun>;
|
Record
用于构造一个对象类型,它所有的key
(键)都是Keys
类型,它所有的value
(值)都是Type
类型。
格式:Record<Keys, Type>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| interface IKun { name: string; age: number; slogan?: string; }
type Keys = keyof IKun; type Res = keyof any;
type MyRecord<Keys extends keyof any, T> = { [P in Keys]: T; };
type Citys = "上海" | "北京" | "洛杉矶";
type IKuns = MyRecord<Citys, IKun>;
const ikuns: IKuns = { 上海: { name: "xxx", age: 10 }, 北京: { name: "yyy", age: 5 }, 洛杉矶: { name: "zzz", age: 3 } };
|
Pick
用于构造一个类型,它是从Type
类型里面挑了一些属性Keys
格式:Pick<Type, Keys>
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface IKun { name: string; age: number; slogan?: string; }
type MyPick<T, K extends keyof T> = { [P in K]: T[P]; };
type IKuns = MyPick<IKun, "slogan" | "name">;
|
Omit
用于构造一个类型,它是从Type
类型里面过滤(排除)一些属性Keys
用法:Omit<Type, Keys>
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface IKun { name: string; age: number; slogan?: string; }
type MyOmit<T, K extends keyof T> = { [P in keyof T as P extends K ? never : P]: T[P]; };
type IKuns = MyOmit<IKun, "slogan" | "name">;
|
Exclude
用于构造一个类型,它是从UnionType
联合类型里面排除了所有可以赋给ExcludedMembers
的类型
用法:Exclude<UnionType, ExcludedMembers>
1 2 3 4 5 6 7
| type IKun = "sing" | "dance" | "rap";
type MyExclude<T, E> = T extends E ? never : T;
type IKuns = MyExclude<IKun, "rap">;
|
用于构造一个类型,它是从Type
类型里面提取了所有可以赋给Union
的类型
用法:Extract<Type, Union>
1 2 3 4 5 6 7
| type IKun = "sing" | "dance" | "rap";
type MyExtract<T, E> = T extends E ? T : never;
type IKuns = MyExtract<IKun, "sing" | "dance">;
|
NonNullable
用于构造一个类型,这个类型从 Type 中排除了所有的null
、undefined
的类型
用法:NonNullable<T>
1 2 3 4 5 6 7
| type IKun = "sing" | "dance" | "rap" | null | undefined;
type MyNonNullable<T> = T extends null | undefined ? never : T;
type IKuns = MyNonNullable<IKun>;
|
ReturnType
用于构造一个含有Type
函数的返回值的类型
用法:ReturnType<T>
1 2 3 4 5 6 7 8 9 10 11
| function iKun() { return "哎呦,你干嘛!"; }
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;
type iKunReturnType = MyReturnType<typeof iKun>;
|
InstanceType
用于构造一个由所有Type
的构造函数的实例类型组成的类型
用法:InstanceType<T>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Person {} class Dog {}
type MyInstanceType<T extends new (...args: any[]) => any> = T extends new ( ...args: any[] ) => infer R ? R : never;
const p1: Person = new Person();
type MyPerson = MyInstanceType<typeof Person>; const p2: MyPerson = new Person();
function factory<T extends new (...args: any[]) => any>(ctor: T): HYInstanceType<T> { return new ctor(); }
const p3 = factory(Person); const d = factory(Dog);
|
Parameters
用于获取函数的所有参数
用法:Parameters<T>
1 2 3 4 5 6 7 8
| type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
function test(a: string, b: number) { return a + b; }
type ParamsType = Parameters<typeof test>;
|
自定义工具
ChangeFunctionReturn
用于改变一个函数的返回值类型
用法:ChangeFunctionReturn<F, R>
1 2 3 4 5 6 7 8 9 10
| type ChangeFunctionReturn<F extends (...args: any[]) => any, R = any> = ( ...args: Parameters<F> ) => R;
function test(a: string, b: number) { return a + b; }
type TestFnType = ChangeFunctionReturn<typeof test, Array<string>>;
|
ChangeFunctionParameters
用于改变一个函数的参数类型
用法:ChangeFunctionParameters<F, P>
1 2 3 4 5 6 7 8 9 10 11
| type ChangeFunctionParameters<F extends (...args: any[]) => any, P extends any[]> = ( ...args: P ) => ReturnType<F>;
function test(a: string, b: number) { return a + b; }
type ParametersType = [x: string, y: number, z: number]; type TestFnType = ChangeFunctionParameters<typeof test, ParametersType>;
|
DeepReadonly
用于构造一个Type
下面的所有属性(包括深层Type
)全都设置为只读的类型
用法:DeepReadonly<F>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| type DeepReadonly<T extends Record<string | symbol, any>> = { readonly [K in keyof T]: DeepReadonly<T[K]>; };
interface IKun { name: string; age: number; hobbies: { sing: string; dance: string; rap: string; }; }
type ReadonlyIKun = DeepReadonly<IKun>;
|
DeepRequired
用于构造一个Type
下面的所有属性(包括深层Type
)全都设置为** 必填的类型**
用法:DeepRequired<T>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| type DeepRequired<T extends object> = { [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P]; };
interface IKun { name: string; age?: number; hobbies: { sing?: string; dance?: string; rap?: string; }; }
type RequiredIKun = DeepRequired<IKun>;
|
Kebab
将小驼峰转换为短横线 kebab-case
用法:Kebab<T>
1 2 3 4 5 6
| type Kebab<T extends string, A extends string = ""> = T extends `${infer F}${infer R}` ? Kebab<R, `${A}${F extends Lowercase<F> ? "" : "-"}${Lowercase<F>}`> : A;
type Str = "getRandomNumber"; type Result = Kebab<Str>;
|
GetOptional
获取对象的可选属性
用法:GetOptional<T>
1 2 3 4 5 6 7 8 9 10 11
| type GetOptional<T extends object> = { [K in keyof T as T[K] extends Required<T>[K] ? never : K]: T[K]; };
interface IKun { name: string; age?: number; hobbies?: []; }
type IKunOptional = GetOptional<IKun>;
|