Typescript 类型系统支持 3 种 可以声明任意类型的变量
type 类型别名
声明一个变量存储某个类型type ttt = Promise<number>;
infer
用于类型提取,然后存到一个变量,相当于局部变量type GetValueType<P> = P extends Promise<infer Value> ? Value : never;
类型参数
接受具体的类型,在类型运算中也相当于局部变量type isTwo<T> = T extends 2 ? true : false;
严格来说三种也不是变量,因为不能被重新赋值 (final)
Typescript 设计可以做类型编程的类型系统目的就是产生各种复杂的类型,不能修改也能产生新的类型的方法是 重新构造重新构造
Type Script 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造新的类型,并且可以在构造新的类型过程中对原类型做一些过滤和变换。
数组类型的重新构造
// Push 给这个元组类型再添加一些类型
type tuple = [1, 2, 3];
// TypeScript 类型变量不支修改,可以构造一个新的元组类型
type Push<Arr extends unknown[], Ele> = [...Arr, Ele];
type PushResult = Push<[1, 2, 3], 4>; // [1, 2, 3, 4]
// Unshift
type Unshift<Arr extends unknown[], Ele> = [Ele, ...Arr];
type UnshiftResult = Unshift<[1, 2, 3], 0>; // [0, 1, 2, 3]
// Zip
type tuple1 = [1, 2];
type tuple2 = ['a', 'b'];
// expect
type tuple = [[1, 'a'], [2, 'b']];
type Zip<One extends [unknown, unknown], Other extends [unknown, unknown]> =
One extends [infer OneFirst, infer OneSecond]
? Ohter extends [infer OtherFirst, infer OtherSecond]
? [[OneFirst, OtherFirst], [OneSecond, OtherSecond]] : []
: [];
// 对于任意个元组合拼,使用递归
type Zip<One extends unknown[], Other extends unknown[]> =
One extends [infer OneFirst, ...infer OneRest]
? Other extends [infer OtherFirst, ...infer OtherRest]
? [[OneFirst, OtherFirst], ...Zip<OneReset, OtherRest>] : []
: [];
字符串类型的重新构造
// CapitalizeStr 'abc' -> 'Abc'
// 1. 类型参数 Str 是待处理字符串,约束为 string
// 2. 通过 infer 提取首个字符到局部变量 First,提取后面字符到局部变量 Rest
// 3. TypeScript 提供的内置高级类型 Uppercase 把首字母转为大写
// 4. 加上 Rest 构成新的字符串类型返回
type CapitalizeStr<Str extends string> =
Str extends `${infer First}${infer Rest}`
? `${Uppercase<First>}${Rest}` : Str;
// CamelCase 'abc_abc_abc' -> 'abcAbcAbc'
// 1. 类型参数 Str 是待处理字符串,约束为 string
// 2. 提取 _ 之前和之后的两个字符到 infer 声明局部变量 Left 和 Right,剩下字符放到 Rest
// 3. 把右边字符 Right 大写,和 Left 构造新的字符串
// 4. 剩余字符 Rest 递归处理
type CamclCase<Str extends string> =
Str extends `${infer Left}_${infer Right}${infer Rest}`
? `${Left}${Uppercase<Right>}${CamelCase<Rest>}`
: Str;
// DropSubStr 'ahahah~~~~', '~' -> 'ahahah'
// 1. 类型参数 Str 待处理字符串,SubStr 要删除字符串,都通过 extends 约束为 string 类型
// 2. 通过模式匹配提取 Substr 前后的字符串到 infer 声明的局部变量 Prefix 和 Suffix
// 3. 匹配用 Prefix、Suffix 构造成新的字符串,递归删除 Substr
// 4. 不匹配返回 Str
type DropSubStr<Str extends string, SubStr extends string> =
Str extends `${infer Prefix}${Substr}${infer Suffix}`
? DropSubStr<`${Prefix}${Suffix}`, SubStr> : Str;
函数类型的重新构造
// AppendArgument
// 1. 类型参数 Func 待处理函数类型,extends 约束为 Function, Arg 要添加的参数类型
// 2. 模式匹配提取参数到 infer 声明局部变量 Args 中,提取返回值到局部变量 ReturnType 中
// 3. 用 Args 数组添加 Arg 构造成新的参数类型,结合 Return Type 构造成新的函数类型返回
type AppendArgument<Func extends Function, Arg> =
Func extends (...args: infer Args) => infer ReturnType
? (...args: [...Args, Arg]) => ReturnType : never;
索引类型的重新构造
索引类型是聚合多个元素的类型
可以添加修饰符 readonly 只读、? 可选
type obj = {
readonly name: string;
age?: number;
gender: boolean;
}
对索引类型的修改和构造新类型涉及映射类型的语法
type Mapping<Obj extends object> = {
[Key in keyof Obj]: Obj[Key];
}
映射过程可对 value 做修改
// Mapping
type Mapping<Obj extends object> = {
[Key in keyof Obj]: [Obj[key], Obj[Key], Obj[Key]];
}
type res = Mapping<{a:1, b:2}>;
/*
type res = {
a: [1, 1, 1];
b: [2, 2, 2];
}
*/
用 keyof 取出 Obj 的索引,作为新的索引类型的索引,也就是 Key in keyof Obj
除了对 value 做修改,也可以对 Key 做修改,使用 as
,叫做重映射
// UppercaseKey
// 因为索引的 Key 可能为 string、number、symbol 类型,而这里只能接受 string 类型,
// 使用 & string 取索引中 string 部分
type UppercaseKey<Obj extends object> = {
[Key in keyof Obj as Uppercase<Key & string>]: Obj[Key];
}
TypeScript 提供高级类型 Record 来创建索引类型type Record<K extends string | number | symbol, T> = { [P in K]: T;}
指定索引和值的类型分别为 K 和 T,就可以创建一个对应的索引类型是,上索引类型用 object,更语义化推荐用 Reacord
// UppercaseKey Record
type UppercaseKey<Obj extends Record<string, any>> = {
[Key in key Obj as Uppercase<Key & string>]: Obj[key];
}
// ToReadonly
// 直接给索引类型添加 readonly 修饰符
type ToReadonly<T> = {
readonly [Key in keyof T]: T[Key];
}
// ToPartial
// 同理索引类型也可以添加可选修饰符
type ToPartial<T> = {
[Key in keyof T]?: T[Key];
}
// ToMutable
// 可以添加 readonly,当然也可以去掉
type ToMutable<T> = {
-readonly [Key in keyof T]: T[Key];
}
// ToRequired
// 同理,也可以去掉可选修饰符
type ToRequired<T> = {
[Key in keyof T]-?: T[Key];
}
// FilterByValueType
// 可以在构造新索引类型的时候根据值的类型做下过滤
type FilterByValueType<
Obj extends Record<string, any>,
ValueType
> = {
[Key in keyof Obj
as ValueType extends Obj[Key] ? Key : never]
: Obj[Key]
}
类型参数 Obj为要处理的索引类型,通过 extends 约束为 string,值为任意类型的索引类型 Record
类型参数 ValueType 为要过滤出的值的类型
构造新的索引类型,索引为 Obj 的索引,也就是 Key in keyof Obj,但要做一些转换,也就是 as 之后的部分
- 如果原来索引的值 Obj[Key] 是 ValueType 类型,索引依然为之前的索引 Key,否则索引设置为 never,never 索引会在生成新的索引类型时被去掉
- 值保持不变,依然为原来索引的值,也就是 Obj[Key]