基础
type
我们定义一个 Point
(二维坐标点)的接口:
interface Point {
x: number;
y: number;
}
当我们想要拓展这个接口,Point3D
(三维坐标点):
interface Point3D extends Point {
z: number;
}
我们也可以将接口映射为 type :
type Point3D = Point & { z: number };
使用 type,我们很容易合并两个 interface:
interface A {
a: string;
}
interface B {
b: string;
}
type AB = A & B;
keyof
keyof 可以将 interface/type 的 key 值返回,返回类型为联合类型(Union Types):
interface Book {
author: string;
numPages: number;
price: number;
}
type BookKeys = keyof Book; // 'author' | 'numPages' | 'price';
内置工具类型
假设我们有个 Person
的接口:
interface Person {
name: string;
age: number;
}
我们想要把某个接口定义为全部可选,或者全部只读:
// 全部可选
interface PersonPartial {
name?: string;
age?: number;
}
// 全部只读
interface PersonReadonly {
readonly name: string;
readonly age: number;
}
可以使用内置工具方法:
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
Typescript 内置了很多 Utility Types:
Partial<Type>
构造的类型为设置所有属性可选(Constructs a type with all properties of Type set to optional )Readonly<Type>
构造的类型为设置所有属性只读(Constructs a type with all properties of Type set to readonly)Record<Keys,Type>
构造一个对象类型,它的key是类型Keys,value是类型Type(Constructs an object type whose property keys are Keys and whose property values are Type)Pick<Type, Keys>
构造的类型为从已有类型Type选择属性Keys (Constructs a type by picking the set of properties Keys from Type)Omit<Type, Keys>
构造的类型为从已有类型Type移除属性Keys (Constructs a type by picking all properties from Type and then removing Keys)Exclude<Type, ExcludedUnion>
构造的类型为从已有联合类型Type移除联合类型ExcludedUnion(Constructs a type by excluding from Type all union members that are assignable to ExcludedUnion)Extract<Type, Union>
构造的类型为从已有联合类型Type提取联合类型Union(Constructs a type by extracting from Type all union members that are assignable to Union)NonNullable<Type>
构造排除null和undefined 的类型(Constructs a type by excluding null and undefined from Type)Parameters<Type>
构造的类型为函数参数的元组类型(Constructs a tuple type from the types used in the parameters of a function type Type)ConstructorParameters<Type>
ReturnType<Type>
构造的类型为函数的返回类型(Constructs a type consisting of the return type of function Type)InstanceType<Type>
Required<Type>
构造的类型设置所有属性必须(Constructs a type consisting of all properties of Type set to required.)ThisParameterType<Type>
OmitThisParameter<Type>
ThisType<Type>
为了方便记忆,我将以上列表整理为表格对比:
Utility Types | 映射类型 | 映射值 | |
---|---|---|---|
映射对象类型 | Partial<Type> |
Type 为对象类型 | 映射为可选(optional) |
Readonly<Type> |
Type 为对象类型 | 映射为只读(readonly) | |
Required<Type> |
Type 为对象类型 | 映射为必须(required) | |
映射对象类型 | Record<Keys,Type> |
Type 为对象类型 Keys 为对象属性 |
映射为key是类型Keys,value是类型Type的对象类型 |
从对象类型中选择/排除 Keys | Pick<Type, Keys> |
Type 为对象类型 Keys 为对象属性 |
映射从Type选择Keys |
Omit<Type, Keys> |
Type 为对象类型 Keys 为对象属性 |
映射从Type移除Keys | |
从联合类型中选择/排除 Union | Exclude<Type,ExcludedUnion> |
Type 为联合类型 ExcludedUnion 为联合类型 |
映射从Type移除ExcludedUnion |
Extract<Type, Union> |
Type 为联合类型 Union 为联合类型 |
映射从Type选择Union | |
映射联合类型 | NonNullable<Type> |
Type 为联合类型 | 映射为不可为Null 或 undefined |
映射函数类型 | Parameters<Type> |
Type 为方法类型 | 映射函数类型的参数 |
ReturnType<Type> |
Type 为方法类型 | 映射函数类型的返回 |
除了前面举例的 Partial
、 Readonly
, Omit
、 Exclude
、 Extract
、 NonNullable
、ReturnType
感觉也非常有用,所以对这几个在以下举例,其他可以到官方文档中查看例子理解:
// NonNullable
type T0 = NonNullable<string | number | undefined>;
// ^ = type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;
// ^ = type T1 = string[]
// ReturnType
type T0 = ReturnType<() => string>;
// ^ = type T0 = string
type T1 = ReturnType<(s: string) => void>;
// ^ = type T1 = void
type T2 = ReturnType<<T>() => T>;
// ^ = type T2 = unknown
declare function f1(): { a: number; b: string };
type T4 = ReturnType<typeof f1>;
// ^ = type T4 = {
// a: number;
// b: string;
// }
此外,定义一个类型,还可以同时使用多个 Utility Types:
interface Book {
author: string | null;
numPages: number;
price: number;
}
type AuthoredBook = Omit<Book, 'author'> & NonNullable<Pick<Book,'author'>>;
// 返回一个 author 不为 null 的类型
// type AuthoredBook = {
// author: string;
// numPages: number;
// price: number;
// }
创建工具类型
TypeScript如何创建工具类型
以 Omit 为例,其实它是两个工具类型结合使用的。
在开始了解之前,我们先来看看如何获取到接口/类型(interface/type)的属性:
interface Demo {
a: string;
b: string;
}
type a = Demo['a']; // 我们可以通过 [] 访问到属性 a
现在我们来看看 Omit 是如何做的:
// definition of Omit
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
//definition of Exclude
type Exclude<T, U> = T extends U ? never : T;
//definition of Pick
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
创建自己的工具类型
现在我们来创建自己的工具类型。我们创建一个工具类,使得对象的所有key和value 都属于某个特定的类型 GenericKeyObject
:
// GenericString:key是string类型,value是类型
type GenericString = { [key: string]: string }
// 把 string 改为泛型
// GenericObject:key是string类型,value是定义的泛型T
type GenericObject<T> = { [key: string]: T }
// GenericKeyObject:key在,value是定义的泛型T ????
type GenericKeyObject<K extends keyof any, T> = {
[P in K]?: T;
};
现在来看看如何使用的:
type BooleanList = GenericKeyObject<string, boolean>; // key 为string类型,值为boolean类型
const countriesVisited: BooleanList = {
France: true,
Italy: true
};
type VisitableCountries = 'Hungary' | 'France' | 'Germany';
type CountriesChecklist = GenericKeyObject<VisitableCountries, boolean>; // key 为VisitableCountries枚举类型,值为boolean类型
const countriesVisitedCurrentYear: CountriesChecklist = {
France: true,
Hungary: true
};
const countriesVisitedLastYear: CountriesChecklist = {
France: true,
Belgium: true
}; // 这个会编译失败,因为Belgium不在VisitableCountries枚举中
工具类型深度使用
用映射类型创建几个微不足道的例子太简单了。现在我们来深度使用工具类型。
Unpacked
我们先来复习几个知识点:
- never 类型:意味着永远没有返回类型
- 类型推断:复习一下官方文档 type inference
- 条件类型:通过表达式判断类型是A还是B,
T extends U ? X : Y
然后我们把这几个概念放在一起玩:
interface ResponseData {
data: string[];
hasMoreItems: boolean;
}
const getData = (): Promise<ResponseData> => {
const data = {
data: ['one', 'two', 'three', 'four'],
hasMoreItems: false
}
return Promise.resolve(data);
}
What if you wanted to unpack the type from the Promise returned by getData? Simple: Create your own utility using infer.
如何创建一个类型 Unpacked
,
type Unpacked<T> = T extends (infer U)[]
? U
: T extends (...args: any[]) => infer U
? U
: T extends Promise<infer U>
? U :
T;
type PromiseResult = Unpacked<ReturnType<typeof getData>>;
// PromiseResult = ResponseData
type FunctionResult = Unpacked<() => string>;
// FunctionResult = string
What’s happening there? You are just inferring the type if T extends a Promise or a Function; otherwise, you are just returning the given type T.
Empty
创建类型 Empty
,当传入空对象,返回true;当传入有属性的接口,返回false。
type Empty<T extends {}> = {} extends Required<T> ? true : false;
type isEmpty = Empty<{}>; // true
type isNotEmpty = Empty<{ name: string}>; // false
这个类型有什么用呢?可以将Empty类型和任何条件类型/映射类型组合成更多的类型,这样可以创建更多的自定义映射类型。
type HttpDataReponse<T> = Empty<T> extends true
? never
: T;
type ClassReponse = HttpDataReponse<{}>; // result be never
type BookReponse = HttpDataReponse<{ author: string}>; // result be { author: string }
https://www.typescriptlang.org/docs/handbook/utility-types.html#instancetypetype
https://www.typescriptlang.org/docs/handbook/advanced-types.html#inference-from-mapped-types
https://www.typescriptlang.org/docs/handbook/type-inference.html
https://medium.com/better-programming/mastering-typescripts-mapped-types-5fa5700385eb
https://itdashu.com/docs/typescriptlesson/2edda/mappedtype.html
https://zhuanlan.zhihu.com/p/45249773