重新构造
大约 3 分钟
引入
类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改
呢?
TypeScript 类型系统支持 3 种可以声明任意类型的变量: type、infer、类型参数
- type 叫做类型别名,其实就是声明一个变量存储某个类型
type test = Promise<number>;
- infer 用于类型的提取,然后存到一个变量里,相当于局部变量
type GetValueType<P> = P extends Promise<infer Value> ? Value : never;
- 类型参数用于接受具体的类型,在类型运算中也相当于局部变量
type isTwo<T> = T extends 2 ? true : false;
TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?
答案是重新构造
重新构造
TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。
数组、字符串、函数等类型的重新构造比较简单。
索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法
数组类型
Push
type arr = [1, 2, 3];
type Push<T extends unknown[], target> = [...T, target];
type result = Push<arr, 4>;
Unshift
type arr = [1, 2, 3];
type Unshift<T extends unknown[], target> = [target, ...T];
type result = Unshift<arr, 4>;
Zip
字符串类型的重新构造
CapitalizeStr
实现 decade
=> Decade
type CapitalizeStr<Str extends string> =
Str extends `${infer First}${infer Rest}`
? `${Uppercase<First>}${Rest}`
: Str;
type result = CapitalizeStr<'decade'>;
CamelCase
实现 xxx_xxx_xxx
=> xxxXxxXxx
DropSubStr
实现 decade_build_w
=> decadebuildw
type DropSubStr<
Str extends string,
DropStr extends string,
> = Str extends `${infer First}${DropStr}${infer Rest}`
? `${First}${DropSubStr<Rest, DropStr>}`
: Str;
type result = DropSubStr<'decade_build_w', '_'>;
函数类型的重新构造
AppendArgument
type AppendArgument<Fn extends Function, AddArg> = Fn extends (
...args: infer Args
) => infer ReturnType
? (...args: [...Args, AddArg]) => ReturnType
: Fn;
type result = AppendArgument<(age: number) => number, string>; // type result = (args_0: number, args_1: string) => number
// 这样是不是没有 具体的名称, 下面是我加强版
type AppendArgumentPlus<
Fn extends Function,
AddArg extends unknown[],
> = Fn extends (...args: infer Args) => infer ReturnType
? (...args: [...Args, ...AddArg]) => ReturnType
: Fn;
type resultPlus = AppendArgumentPlus<(age: number) => number, [name: string]>;
索引类型的重新构造
Mapping
type Mapping<Obj extends Record<string, any>>> = {
[Key in keyof Obj]: [Obj[Key], Obj[Key], Obj[Key]];
};
UppercaseKey
type UppercaseKey<Obj extends Record<string, any>>> = {
[Key in keyof Obj as Uppercase<Key & string>]: Obj[Key];
};
ToReadonly
type ToReadonly<T> = {
readonly [Key in keyof T]: T[Key];
};
ToPartial
type ToPartial<T> = {
[Key in keyof T]?: T[Key];
};
ToMutable
type ToMutable<T> = {
-readonly [Key in keyof T]: T[Key];
};
ToRequired
type ToRequired<T> = {
[Key in keyof T]-?: T[Key];
};
FilterByValueType
// never 的索引会在生成新的索引类型时被去掉
type FilterByValueType<Obj extends Record<string, any>, ValueType> = {
[Key in keyof Obj as Obj[Key] extends ValueType ? Key : never]: Obj[Key];
};