《TypeScript全面进阶指南》 的不完全速记笔记.
泛型
泛型作为类型编程中的参数,可以应用在类型别名、对象类型、函数与Class中
类型别名中的泛型
ts
// 类似工厂函数的工厂类型
type Factory<T> = T | number | string;
// 通过索引签名将一个对象类型的所有属性类型设置为string
type Stringify<T> = {
[k in keyof T]: string;
}
// 通过类型映射创建一个对应对象全部属性的类型
type Clone<T> = {
[K in keyof T]: T[K];
}
// 通过类型映射创建一个可选的变种类型
type Partial<T> = {
[P in keyof T]?: T[P];
}
// 条件类型: T enxtends Condition | Type extends T
type IsEqual<T> = T extends true ? 1 : 2;
type A = IsEqual<true>; // 1
type B = IsEqual<false>; // 2
type C = IsEqual<'something'>; // 2
泛型约束与默认值
ts
// 与函数默认值类似
type Factory<T = boolean> = T | number | string;
const foo: Factory = false;
// 使用extends进行泛型约束
type ResStatus<ResCode extends number> = ResCode extends 1000 | 1001 | 1002 ? 'success' : 'failure';
type Res1 = ResStatus<1000>; // 'success'
type Res2 = ResStatus<1001>; // 'failure'
type Res3 = ResStatus<'1002'>; // 类型'string'不满足约束'number'
// 类型约束增加默认值
type ResStatus2<ResCode extends number = 1000> = ResCode extends 1000 | 1001 | 1002 ? 'success' : 'failure';
type Res4 = ResStatus2;
多泛型关联
在传入多个泛型参数的同时,还可以让这几个泛型参数之间也存在联系
ts
// 多泛型参数多用于需要在内部进行逻辑运算的情形
type Conditional<Type, Condition, TruthyResult, FalsyResult> = Type extends Condition ? TruthyResult : FalsyResult;
type Result1 = Conditional<'huhu', string, 'success!', 'rejected!'>; // success!
type Result2 = Conditional<'huhu', number, 'success!', 'rejected!'>; // rejected!
/**
* 多泛型参数之间的依赖,是指在后续泛型参数中,使用前面的泛型参数作为约束或默认值
* 下例有如下含义:
* 当只传入一个泛型参数时,其第二个泛型参数会被赋值为此参数,而第三个则会赋值为第二个泛型参数,相当于均使用了这唯一传入的泛型参数
* 当传入两个泛型参数时,第三个泛型参数会默认赋值为第二个泛型参数的值
*/
type ProcessInput<
Input,
SecondInput extends Input = Input,
ThirdInput extends Input = SecondInput
> = number;
对象类型中的泛型
ts
// 预留实际响应数据的泛型坑位实现通用数据结构
interface IRes<TData = unknown> {
code: number;
error?: string;
data: TData;
}
/* 下面是使用泛型嵌套的例子 */
interface IUserProfileRes {
name: string;
homepage: string;
avatar: string;
}
function fetchUserProfile(): Promise<IRes<IUserProfileRes>> {
//...
}
type StatusSucceed = boolean;
function handleOperation(): Promise<IRes<StatusSucceed>> {
//...
}
// 分页结构数据情形
interface IPaginationRes<TItem = unknown> {
data: TItem[];
page: number;
totalCount: number;
hasNextPage: boolean;
}
function fetchUserProfileList(): Promise<IRes<IPaginationRes<IUserProfileRes>>> {
//...
}
函数中的泛型
ts
/**
* 当一个函数接受多个参数并对它进行处理时,按照以往的学习可以使用如下方式
*/
// 使用any,缺点:失去类型推断能力,不推荐
function handler(input: any): any {}
// 使用联合类型,缺点:每次增加类型都需要手动添加
function handler(input: string | number | {}): string | number | {} {}
// 使用函数重载,缺点:和手动添加联合类型类似机械且难以维护
function handler(input: string): string
function handler(input: number): number
function handler(input: {}): {}
function handler(input: string | number | {}): string | number | {} {}
// 使用泛型,既解决了类型预设的麻烦(范性在调用时被填充)又可以对返回值和参数进行关联运算
function handler<T>(input: T): T {
return input;
}
const name = 'someName';
const age = 17;
handler(name); // infer to string
handler(age); // infer to number
// 范型参数被内部逻辑消费
function handler<T>(payload: T): Promise<[T]> {
return new Promise<[T]>((res, rej) => {
res([payload]);
});
}
class中的范型
与函数范型不同的是,函数是参数和返回值对范型进行消费。而Class则是属性、方法、乃至装饰器等
ts
class Queue<TElementType> {
private _list: TElementType[];
constructor(initial: TElementType[]) {
this._list = initial;
}
// 入队一个队列泛型子类型的元素
enqueue<TType extends TElementType>(ele: TType): TElementType[] {
this._list.push(ele);
return this._list;
}
// 入队一个任意类型元素(无需为队列泛型子类型)
enqueueWithUnknownType<TType>(element: TType): (TElementType | TType)[] {
return [...this._list, element];
}
// 出队
dequeue(): TElementType[] {
this._list.shift();
return this._list;
}
}
内置方法的范型
ts
// 为Promise设置范型时,范型会自动填充到resolve的参数中
function p() {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
// 在Typescript内部Promise这么定义
interface PromiseConstructor {
resolve<T>(value: T | PromiseLike<T>): Promise<T>;
}
declare var Promise: PromiseConstructor;
// 数组Array<T>
const arr: string[] = ['aaa', 'bbb', 'ccc'];
arr.push[1]; // Argument of type 'number' is not assignable to parameter of type 'string'
arr.find(() => false); // number | undefined
// 如果初始值的类型与数组元素类型一致,则使用数组的元素类型进行填充
arr.reduce((prev, curr, idx, arr) => {
return prev;
}, 1);
// reduce 的泛型参数会默认从这个初始值推导出的类型进行填充,如这里是 never[]
// 报错:不能将 number 类型的值赋值给 never 类型
arr.reduce((prev, curr, idx, arr) => {
return [...prev, curr]
}, []);
// 手动指定类型
arr.reduce<number[]>((prev, curr, idx, arr) => {
return prev;
}, []);