TS类型技巧(二):构造
TS类型系统不能对原类型进行修改,而是每次都构造新类型
extends + infer 提取类型,下一步便是 构造新类型
1. 数组:解构
解构,只需要掌握解构语法
type Push<Arr extends unknown[], Ele> = [...Arr, Ele];
type Zip<
T extends [unknown, unknown],
U extends [unknown, unknown]
> = T extends [infer A, infer B]
? U extends [infer C, infer D]
? [[A, C], [B, D]]
: never
: never
// [[1, 'a'], [2, 'b']];
type tuple1 = Zip<[1, 2], ['a', 'b']>
// 任意个元组类型构造Zip2(递归)
type Zip2< T extends unknown[], U extends unknown[] > =
T extends [infer FT, ...infer RT]
? U extends [infer FU, ...infer RU]
? [[FT, FU], ...Zip2<RT, RU>]
: []
: []
// [[1, 4], [2, 5], [3, 6]]
type testZip2 = Zip2<[1, 2, 3], [4, 5, 6]>
字符串:解构,正则,内置工具
解构,正则,内置工具类
下划线改驼峰
type CamelCase<Str extends string> =
Str extends `${infer Left}_${infer Right}${infer Rest}`
? `${Left}${Uppercase<Right>}${CamelCase<Rest>}`
: Str;
// "helloWorldTest"
type camelCase = CamelCase<'hello_world_test'>;
函数:解构
解构
// 加一个参数
type AppendArgument<T extends Function, arg> =
T extends (...args: infer Args) => infer ReturnType ?
(...args: [...Args, arg]) => ReturnType : never;
type appendArgument = AppendArgument<() => void, boolean>; // (args_0: boolean) => void
索引:keyof,in,as
keyof 生成Key的联合类型
in 进行key遍历,以修改value
as 修改key
修改value
type obj = {
name: string;
age: number;
gender: boolean;
}
// 基础用法,可在此基础上修改value
type Mapping<Obj extends object> = {
[Key in keyof Obj]: Obj[Key]
}
// { name: string; age: number; gender: boolean }
type mapping = Mapping<obj>;
修改key, as 重命名
as 修改key叫做 Key Remapping(键重映射)
type UppercaseKey<Obj extends object> = {
[Key in keyof Obj as Uppercase<Key & string>]: Obj[Key]
}
// { NAME: string; AGE: number; GENDER: boolean; }
type uppercaseKey = UppercaseKey<obj>
type Record<K extends keyof any, T> = {
[P in K]: T;
}
// { [x: string]: number; [x: number]: number }
type record = Record<string | number, number>;
// 有了record之后,可以更具体约束为 key 为 string,值为任意类型的索引类型
type UppercaseKey2<Obj extends Record<string, any>> = {
[Key in keyof Obj as Uppercase<Key & string>]: Obj[Key]
}
& string
是 通用写法, Key 可能为string、number、symbol, 代表只取string
<K extends keyof any>
约束 K 是可以作为对象键的类型
K extends string | number | symbol
与这条完全相等
Record<string, any>
Record
专门用来创建索引类型,class、对象 等都是 索引类型
Obj extends Record<string, any>
对比 Obj extends Object
后者 无法确保是“纯粹的对象字面量结构”,
string, number, boolean, array, function
都属于 Object
以后都可以用Record<string, any>
代替Obj extends Object
另外 <T, Key extends keyof T>
也是常见写法,
此时 Key 为 string | number | symbol
, 使用 Uppercase需要Uppercase<Key & string>
删除key, as 条件类型
// 过滤只保留指定value类型的key
type FilterByValueType<
Obj extends Record<string, any>,
ValueType
> = {
[Key in keyof Obj as Obj[Key] extends ValueType
? Key
: never]: Obj[Key]
}
// { a: string }
type filterByValueType = FilterByValueType<
{ a: string; b: number },
string
>
Key in keyof Obj
遍历Obj的key,用 as 修改key
这些key 如果 extends ValueType
,则保留该key,否则剔除
修改修饰符
type ToReadonly<T> = {
readonly [Key in keyof T]: T[Key];
}
type ToPartial<T> = {
[Key in keyof T]?: T[Key]
}
type ToMutable<T> = {
-readonly [Key in keyof T]: T[Key]
}
type ToRequired<T> = {
[Key in keyof T]-?: T[Key]
}
联合类型 归一化技巧
// 删除指定属性的readonly修饰符
type RemoveReadonly<
T extends Record<string, any>,
K extends keyof T
> =
// 1. 将 K 中的属性设为可变(移除 readonly)
{ -readonly [P in K]: T[P] }
// 2. 保留非 K 的其它属性(原样)
& { [P in Exclude<keyof T, K>]: T[P] }
// 对联合类型归一化
type RemoveReadonly2<
T extends Record<string, any>,
K extends keyof T
> =
{ -readonly [P in K]: T[P] }
& { [P in Exclude<keyof T, K>]: T[P] } extends infer O
// 3. 重新整理属性顺序(避免交叉类型不易使用)
? { [P in keyof O]: O[P] }
: never
// { name: string; } & { age: number; }
type test1 = RemoveReadonly<
{ readonly name: string; age: number },
'name'
>
// { name: string; age: number; }
type test2 = RemoveReadonly2<
{ readonly name: string; age: number },
'name'
>
extends infer O
这里是小type推导出大type,而之前常见的是大type中infer提取小type
P in Exclude<keyof T, K>
属于是 K in keyof T
高阶用法,对T进行了先一步过滤,再遍历
type Exclude<T, U> = T extends U ? never : T
T中排除U
type Extract<T, U> = T extends U ? T : never
T中只保留U
上面两个内置类型同时还用到了 联合类的分发策略
其他
extends 就是 if条件判断
infer 就是 if条件内进行 解构+变量声明
递归 就是 for循环
in 是遍历
keyof 是解构索引类型
as 键重映射
神光大佬的TypeScript 类型体操通关秘籍