# 类型别名中的泛型
类型别名中如果声明了泛型占位,那其实就等价于一个接受参数的函数:
代码语言:javascript复制type Factory<T> = T | number | string;
// 伪代码理解
// function Factory(typeArg) {
// return typeArg | number | string;
// }
类型别名中的泛型大多是用来进行工具类型封装:
代码语言:javascript复制type Stringify<T> = {
[K in keyof T]: string;
}
type Clone<T> = {
[K in keyof T]: T[K];
}
TypeScript 内置工具类型 Partial
就是使用了类型别名:
type Partial<T> = {
[K in keyof T]?: T[K];
}
interface IFoo {
prop1: string;
prop2: number;
prop3: boolean;
}
type PartialIFoo = Partial<IFoo>;
// 等价于
// interface PartialIFoo {
// prop1?: string;
// prop2?: number;
// prop3?: boolean;
// }
在条件类型参与的情况下,通常泛型会被作为条件类型中的判断条件(T extends Condition
或 Type extends T
)以及返回值。这也是筛选类型需要依赖的能力之一。
type IsEqual<T> = T extends true ? 1 : 0;
type IsEqualTrue = IsEqual<true>; // 1
type IsEqualFalse = IsEqual<false>; // 0
# 泛型约束与默认值
泛型也有默认值的设定:
代码语言:javascript复制type Factory<T = boolean> = T | number | string;
// 在调用时不带任何参数,就会使用默认值
const foo: Factory = false;
// 伪代码理解
// function Factory(typeArg = boolean) {
// return typeArg | number | string;
// }
泛型约束,可以要求传入这个工具类型的泛型必须符合某些条件,否则就拒绝进行后面的逻辑:
代码语言:javascript复制type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002
? 'success'
: 'error';
type Res1 = ResStatus<10000>; // 'success'
type Res2 = ResStatus<10003>; // 'error'
type Res3 = ResStatus<'10000'>; // Type 'string' does not satisfy the constraint 'number'.
extends
关键字用来约束传入的泛型参数必须符合要求。A extends B
表示 A
是 B
的子类型,或者说 A
比 B
的类型更精确、更复杂(实现细节上)。
给泛型参数声明一个默认值,可以让这个泛型参数变成可选的:
代码语言:javascript复制type ResStatus<ResCode extends number = 10000> = ResCode extends 10000 | 10001 | 10002
? 'success'
: 'error';
type Res1 = ResStatus<10000>; // 'success'
type Res2 = ResStatus<10003>; // 'error'
type Res3 = ResStatus; // 'success'
# 多泛型关联
不仅可以同时传入多个泛型参数,还可以让这几个泛型参数之间也存在联系:
代码语言:javascript复制type Conditional<Type, Condition, TruthyResult, FalsyResult> =
Type extends Condition ? TruthyResult : FalsyResult;
type Result1 = Conditional<'Cell', string, 'passed!', 'rejected!'>; // 'passed!'
type Result2 = Conditional<100, string, 'passed!', 'rejected!'>; // 'rejected!'
多泛型参数像接受多个参数的函数,其内部运行逻辑(类型操作)会更加抽象,表现在参数(泛型参数)需要进行的逻辑运算(类型操作)会更加复杂。
# 对象类型中的泛型
由于泛型提供了对类型结构的复用能力,也会经常在对象类型结构中使用泛型:
代码语言:javascript复制interface IRes<IData = unknown> {
code: number;
error?: string;
data: IData;
}
interface IUserProfileRes {
name: string;
age: number;
}
function fetchUserProfile(): Promise<IRes<IUserProfileRes>> {}
在分页结构中,也常用到泛型嵌套:
代码语言:javascript复制interface IPaginationRes<IItem = unknown> {
data: IItem[];
total: number;
page: number;
hasMore: boolean;
}
function fetchUserProfileList(): Promise<IRes<IPaginationRes<IUserProfileRes>>> {}
# 函数中的泛型
代码语言:javascript复制function handle<T>(input: T): T {}
T
会被自动地填充为这个参数的类型,不再需要预先确定参数的可能类型,在返回值与参数类型关联的情况下,也可以通过泛型参数来进行运算。
在基于参数类型进行填充泛型时,其类型信息会被推断到尽可能精确的程度。因为传入一个值时,这个值是不会再被修改的,因此可以推导到最精确的程度。如果使用变量作为参数,那么会使用这个变量标注的类型。
代码语言:javascript复制function handle<T>(input: T): T {}
const developer = 'Cell';
let age = 18;
handle(developer); // 'Cell' 填充字面量类型为 'Cell'
handle(age); // number 填充变量类型为 number
箭头函数中的泛型:
代码语言:javascript复制const handle = <T>(input: T): T => {};
在 tsx 中,泛型尖括号可能造成报错,可以让他看起来明显一点:
代码语言:javascript复制const handle = <T extends T>(input: T): T => {};
# Class 中的泛型
Class 中的泛型和函数中的泛型类似,只是函数中泛型参数的消费方式参数和返回值类型,Class 中的泛型消费方则是属性、方法、乃至装饰器等。
Class 内的方法还可以再声明自己独有的泛型参数。
代码语言:javascript复制class Queue<TElementType> {
private _list: TElementType[];
constructor (initial: TElementType[]) {
this._list = initial;
}
enqueue<IType extends TElementType>(ele: IType): TElementType[] {
this._list.push(ele);
return this._list;
}
enqueueWithUnkonwType<IType>(element: IType): (TElementType | IType)[] {
this._list.push(element);
return this._list;
}
dequeue(): TElementType [] {
this._list.shift();
return this._list;
}
}
# 内置方法中的泛型
TypeScript 中为非常多的内置对象都预留了泛型占位,如 Promise
:
function p() {
return new Promise<boolean>((resolve, reject) => {
// 填充 Promise 泛型后,其内部的 resolve 方法也自动填充了泛型
resolve(true);
});
}
还有数组 Array<T>
当中,其泛型参数代表数组的元素类型,几乎贯穿所有的数组方法:
const arr: Array<number> = [1, 2, 3];
arr.push('Cell'); // Argument of type '"Cell"' is not assignable to parameter of type 'number'.