前言
TypeScript 内置了一些常用的工具类型,这些工具类型大大方便了开发人员,在学习工具类型之前,我们需要先了解一下高级类型的用法。
高级类型
不同于基础类型,高级类型方便我们灵活的操作和定义类型。
交叉类型(&)
交叉类型是将多个类型合并为一个类型。
type PersonType = { name: string; id: number } & { age: number }
const person: PersonType = {
id: 1,
name: 'lisi',
age: 18
}
联合类型(|)
联合类型表示取值可以为多种类型中的一种.
interface Button {
type: 'default' | 'primary' | 'danger'
text: string
}
const btn: Button = {
type: 'primary', // type 可以选择 'default'、'primary'、'danger' 任意其中一个
text: '按钮'
}
类型别名(type)
前面提到的交叉类型与联合类型如果有多个地方需要使用,就需要通过类型别名的方式,给这两种类型声明一个别名。
type ButtonType = type: 'default' | 'primary' | 'danger'
类型索引(keyof)
keyof 类似于 Object.keys ,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
interface Person {
name: string;
age: number;
id?: number;
}
type K1 = keyof Person // name | age | id
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number
类型约束(extends)
extends 可以用来继承一个类,也可以用来继承一个 interface,但还可以用来判断有条件类型,类型约束简单点说就是把类型的具体化数据类型范围缩小。
type Words = 'a'|'b'|"c";
type W<T> = T extends Words ? true : false;
type WA = W<'a'>; // -> true
type WD = W<'d'>; // -> false
类型映射(in)
in 用来遍历枚举类型。
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any, c: any }
条件类型(U ? X : Y)
条件类型的语法规则和三元表达式一致,经常用于一些类型不确定的情况。
type Extract<T, U> = T extends U ? T : never;
infer
infer表示在extends条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来。
infer 出现的位置:
infer出现在extends条件语句后的函数类型的参数类型位置上infer出现在extends条件语句后的函数返回值类型上infer出现在类型的泛型具体化上
type ParamType<T> = T extends (...args: infer P) => any ? P : T;
interface User {
name: string;
age: number;
}
type Func = (user: User) => void;
type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string
内置类型
下面我们看一下 TS 内置了哪些工具范型。
Partial
Partial 的作用就是可以将某个类型里的属性全部变为可选项 ?。
源码:
type Partial<T> = {
[P in keyof T]?: T[P];
};
示例:
interface IUser {
name: string;
age: number;
sex: number;
}
type UserType = Partial<IUser>
type UserType = {
name?: string | undefined;
age?: number | undefined;
sex?: number | undefined;
}
Required
Required 的作用刚好跟 Partial 相反,Partial 是将所有属性改成可选项,Required 则是将所有类型改成必选项。
源码:
type Required<T> = {
[P in keyof T]-?: T[P];
};
示例:
type UserType2 = Required<UserType>
type UserType2 = {
name: string;
age: number;
sex: number;
}
其中 -? 是代表移除 ? 这个 modifier 的标识。
与之对应的还有个 +? , 这个含义自然与 -? 之前相反, 它是用来把属性变成可选项的,+ 可省略,见 Partial。
Readonly
这个类型的作用是将传入的属性变为只读选项。
源码:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
示例:
type UserType3 = Readonly<UserType>
type UserType3 = {
readonly name: string;
readonly age: number;
readonly sex: number;
}
给子属性添加 readonly 的标识,如果将上面的 readonly 改成 -readonly, 就是移除子属性的 readonly 标识。
Pick
这个类型则可以将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
源码:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
示例:
interface Todo {
title: string
completed: boolean
description: string
}
type TodoPreview = Pick<Todo, "title" | "completed">
const todo: TodoPreview =
title: 'Clean room',
completed: false
}
Record
该类型可以将 K 中所有的属性的值转化为 T 类型。
源码:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
示例:
type T = Record<'a' | 'b' | 'c', Person>; // -> { a: Person; b: Person; c: Person; }
Exclude
Exclude 将某个类型中属于另一个的类型移除掉。
源码:
type Exclude<T, U> = T extends U ? never : T;
示例:
type T1 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // -> 'b' | 'd'
Extract
Extract 的作用是提取出 T 包含在 U 中的元素,换种更加贴近语义的说法就是从 T 中提取出 U。
源码:
type Extract<T, U> = T extends U ? T : never
示例:
type T2 = Extract<string | number | symbol, string | number> // string | number
ReturnType
该类型的作用是获取函数的返回类型。
源码:
type ReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => infer R ? R : any;
示例:
function bar(x: string): Array<string> {
return [x];
}
type fn = ReturnType<typeof bar>; // -> string[]
ThisType
这个类型是用于指定上下文对象类型的。
源码:
interface ThisType<T> { }
声明中只有一个接口,没有任何的实现,说明这个类型是在 TS 源码层面支持的,而不是通过类型变换。
示例:
interface Person {
name: string;
age: number;
}
const obj: ThisType<Person> = {
dosth() {
this.name // string
}
}
这样的话,就可以指定 obj 里的所有方法里的上下文对象改成 Person 这个类型了。
需要注意的是使用 ThisType<T> 时,必须确保 --noImplicitThis 标志设置为 true。
InstanceType
该类型的作用是获取构造函数类型的实例类型。
源码:
type InstanceType<T extends new (...args: any[]) => any> =
T extends new (...args: any[]) => infer R ? R : any;
示例:
class C {
x = 0;
y = 0;
}
type T1 = InstanceType<typeof C>; // C
type T2 = InstanceType<any>; // any
type T3 = InstanceType<never>; // any
type T4 = InstanceType<string>; // Error
type T5 = InstanceType<Function>; // Error
NonNullable
这个类型可以用来过滤类型中的 null 及 undefined 类型。
源码:
type NonNullable<T> = T extends null | undefined ? never : T;
示例:
type T1 = string | number | null;
type T2 = NonNullable<T1>; // -> string | number;
Parameters
该类型可以获得函数的参数类型组成的元组类型。
源码:
type Parameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;
示例:
function bar(x: string): Array<string> {
return [x];
}
type P = Parameters<typeof bar>; // -> [number]
此时 P 的真实类型就是 foo 的参数组成的元组类型 [number]。
ConstructorParameters
该类型的作用是获得类的参数类型组成的元组类型,简单获取类构造函数参数类型。
源码:
type ConstructorParameters<T extends new (...args: any[]) => any> =
T extends new (...args: infer P) => any ? P : never;\
示例:
class Person {
private firstName: string;
private lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
type P = ConstructorParameters<typeof Person>; // -> [string, string]
自定义常用类型
下面是一些比较实用的非内置范型工具类型。
Omit
Omit<T, K extends keyof any> 的作用是使用 T 类型中除了 K 类型的所有属性,来构造一个新的类型。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
示例:
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, "description">;
const todo: TodoPreview = {
title: "room",
completed: false,
};
Mutable
用来将所有属性的readonly移除:
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
}
Deferred
相同的属性名称,但使值是一个 Promise,而不是一个具体的值:
type Deferred<T> = {
[P in keyof T]: Promise<T[P]>;
};
DeepReadonly
DeepReadonly用来深度遍历 T,并将其所有属性变成只读类型。
源码:
type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }
示例:
type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
interface A {
B: { C: number; };
D: { E: number; }[];
}
const myDeepReadonlyObject: DeepReadonly<A> = {
B: { C: 1 },
D: [ { E: 2 } ],
}
myDeepReadonlyObject.B = { C: 2 }; // error :)
myDeepReadonlyObject.B.C = 2; // error :)
约束变量的深层属性为只读。
ConvertNumberToString
ConvertNumberToString用来将number转换为string类型。
type ConvertNumberToString<T> = {
[K in keyof T]: T[K] extends string ? string : T[K]
}
ValueOf
ValueOf与keyof相对应。取出指定类型的所有 value。
type ValueOf<T> = T[keyof T]
PowerPartial
内置的 Partial 有个局限性,就是只支持处理第一层的属性,如果是嵌套多层的就没有效果了,不过可以如下自定义:
type PowerPartial<T> = {
// 如果是 object,则递归类型
[U in keyof T]?: T[U] extends object
? PowerPartial<T[U]>
: T[U]
};
Deferred
相同的属性名称,但使值是一个 Promise,而不是一个具体的值:
type Deferred<T> = {
[P in keyof T]: Promise<T[P]>;
};
Proxify
为 T 的属性添加代理:
type Proxify<T> = {
[P in keyof T]: { get(): T[P]; set(v: T[P]): void }
};
PromiseReturnType
获取Promise的返回值类型。
源码:
type PromiseReturnType<T extends Promise<any>> =
T extends Promise<infer R> ? R : any;
示例:
type MyPromise = Promise<string>
type T = PromiseReturnType<MyPromise> /// string
Overwrite
后一个类型重新覆盖前一个类型已有的参数。
源码:
type Overwrite<T, U> = {
[K in keyof T]: K extends keyof U ? U[K] : T[K];
};
示例:
type Props = { name: string; age: number; visible: boolean };
type NewProps = { age: string; other: string };
type ReplacedProps = Overwrite<Props, NewProps>;
// { name: string; age: string; visible: boolean; }