一. 体操基本原理(Type Challenge)
- 最简单的例子
type A = 1;
type B = 1 | 2;
type Result = A extends B ? true : false; // true
- 两个条件的例子
type A = 1;
type B = 1 | 2;
type C = 3;
type D = 3 | 4;
type Result = A extends B
? C extends D
? 'true, true'
: 'true, false'
: C extends D
? 'false, true'
: 'false, false';
// true, true
- 判断是否是空元组类型的例子
type A = [];
type isEmptyArray<Arr extends unknown[]> = Arr['length'] extends 0
? true
: false;
type Result = isEmptyArray<A>; // true
- 使用infer,判断非空数组的例子
type A = [1];
type NotEmpty<Arr extends unknown[]> = Arr extends [...infer X, infer Last]
? // [...infer X, infer Last] 这个可以理解成[...unknown[], unknown],满足这个条件表示至少有一个元素, infer表示后面跟的变量是类型
true
: false;
type Result = NotEmpty<A>;
- 递归的例子
type A = ['ji', 'ni', 'tai', 'mei'];
type Reverse<Arr extends unknown[]> = Arr extends [...infer Rest, infer Last]
// Rest可以是[]
? [Last, ...Reverse<Rest>]
: Arr;
type Result = Reverse<A>; // ['mei', 'tai', 'ni', 'ji']
- 模式匹配 + infer"引用"的例子
type Tuple = ['ji', 'ni', 'tai', 'mei'];
type Result1 = Tuple extends [infer First, ...infer Rest] ? First : never; // 'ji'
// 如果没用到也可以写成 Tuple extends [infer First, ...String[]]
type Result2 = Tuple extends [infer First, ...infer Rest] ? Rest : never; // 'ni', 'tai', 'mei'
// 如果没用到也可以写成 Tuple extends [String, ..infer Rest]
二. 元组体操
- 元组扩展的例子
type A = [1]
type B = [...A, 2]
- 两个元组相加
type B = [1, 2];
type C = [3, 4];
type D = [...B, ...C];
- 返回元组最后一项
type D = [1, 2, 3, 4];
type Last<T extends unknown[]> = T extends [...unknown[], infer L] ? L : never;
type E = Last<D>;
- 拿到除了最后一项的其它项
type D = [1, 2, 3, 4];
type NoLast<T> = T extends [...infer X, unknown] ? X : never;
type E = NoLast<D>;
三. 字符串的相关体操
- 首字母大写例子
type A = 'fang';
type B = Capitalize<A>;
// "Fang"
type C = 'ji' | 'ni' | 'tai' | 'mei';
type X = Capitalize<C>;
// "Ji" | "Ni" | "Tai" | "Mei"
-
其它的几个内置
Uppercase,Lowercase,Uncapitalize- Uppercase 全大写
- Lowercase 全小写
- Uncapitalize 首字母小写
-
模板字符串,字符串连接例子
type A = 'ji';
type B = 'ni';
type C = 'tai';
type D = 'mei';
type X = `${A} ${B} ${C} ${D}`;
// "ji ni tai mei"
- 获取字符串第一个自符的例子
type A = 'ji ni tai mei';
type First<T extends string> = T extends `${infer F}${string}` ? F : never;
type Result = First<A>; // j
- 归获取字符串最后一个字符,通过将字符串转成远组来做
type A = 'ji ni tai mei';
type LastOfTuple<T extends unknown[]> = T extends [...infer _, infer L]
? L
: never;
type StringToTuple<S extends string> = S extends `${infer F}${infer R}`
? [F, ...StringToTuple<R>]
: [];
type LastOfString<S extends string> = LastOfTuple<StringToTuple<S>>;
type R = LastOfString<'ji ni tai mei x'>;
四. infer的文档
- 在条件类型这一章,也能说明infer通常和extends结合: www.typescriptlang.org/zh/docs/han…
- 文档上的例子,infer就是显式的指定一个泛型变量
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
type X = Flatten<Array<number>>; // number
五. ts中的递归层数限制
- 根据测试,最多递归48
type A = [
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
'ji', 'ni', 'tai', 'mei',
];
// 再多加一项就会报错
type Reverse<Arr extends unknown[]> = Arr extends [...infer Rest, infer Last]
? // Rest可以是[]
[Last, ...Reverse<Rest>]
: Arr;
type Result = Reverse<A>; // ['mei', 'tai', 'ni', 'ji']
- readonly的对象层数最多一层
- 但是可以自己去写一个递归的readonly
- 这里估计也是48层
interface SomeObject {
a: {
b: {
c: number;
};
};
}
const obj: Readonly<SomeObject> = {
a: {
b: {
c: 1,
},
},
};
obj.a.b.c = 2; // Readonly只有一层,这样不报错
type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
const obj2: DeepReadonly<SomeObject> = {
a: {
b: {
c: 1,
},
},
};
obj2.a.b.c = 3; // 这样会报错
六. 数组和联合类型之间的转换
- 也是用递归去做,参考之前提到的
stringToTuple的写法
type StringToUnion<S extends string> = S extends `${infer First}${infer Rest}`
? First | StringToUnion<Rest>
: never;
type Result = StringToUnion<'jinitaimei'>;