TypeScript 类型体操
一、类型体操基础
1.1 什么是类型体操
类型体操是指利用 TypeScript 类型系统的高级特性(泛型、条件类型、映射类型、模板字符串类型等)来实现复杂的类型转换和类型计算。
1.2 核心技巧
- 递归类型:处理嵌套结构
- 条件类型 + infer:类型推断与提取
- 映射类型:批量转换属性
- 模板字符串类型:字符串操作
二、经典类型挑战
2.1 实现 Pick
typescript
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
// 测试
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>;
// { title: string; completed: boolean }2.2 实现 Readonly
typescript
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};2.3 实现 Exclude
typescript
type MyExclude<T, U> = T extends U ? never : T;
type Result = MyExclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'2.4 实现 Omit
typescript
type MyOmit<T, K extends keyof any> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
// 或者
type MyOmit2<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;2.5 实现 ReturnType
typescript
type MyReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : never;2.6 实现 Parameters
typescript
type MyParameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;三、中级挑战
3.1 获取数组第一个元素类型
typescript
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type arr = [3, 2, 1];
type head = First<arr>; // 33.2 获取数组最后一个元素类型
typescript
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type tail = Last<[3, 2, 1]>; // 13.3 获取数组长度
typescript
type Length<T extends readonly any[]> = T['length'];
type len = Length<[1, 2, 3]>; // 33.4 实现 Concat
typescript
type Concat<T extends any[], U extends any[]> = [...T, ...U];
type Result = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]3.5 实现 Push / Unshift
typescript
type Push<T extends any[], U> = [...T, U];
type Unshift<T extends any[], U> = [U, ...T];3.6 实现 Includes
typescript
type Includes<T extends readonly any[], U> =
T extends [infer First, ...infer Rest]
? Equal<First, U> extends true
? true
: Includes<Rest, U>
: false;
// Equal 工具类型
type Equal<X, Y> =
(<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
? true
: false;3.7 实现 Flatten
typescript
type Flatten<T extends any[]> =
T extends [infer First, ...infer Rest]
? First extends any[]
? [...Flatten<First>, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: [];
type Result = Flatten<[1, [2, [3, 4]], 5]>; // [1, 2, 3, 4, 5]四、字符串类型挑战
4.1 实现 Trim
typescript
type Space = ' ' | '\n' | '\t';
type TrimLeft<S extends string> =
S extends `${Space}${infer R}` ? TrimLeft<R> : S;
type TrimRight<S extends string> =
S extends `${infer R}${Space}` ? TrimRight<R> : S;
type Trim<S extends string> = TrimRight<TrimLeft<S>>;
type Result = Trim<' hello world '>; // 'hello world'4.2 实现 Replace
typescript
type Replace<
S extends string,
From extends string,
To extends string
> = From extends ''
? S
: S extends `${infer L}${From}${infer R}`
? `${L}${To}${R}`
: S;
type Result = Replace<'hello world', 'world', 'TS'>; // 'hello TS'4.3 实现 ReplaceAll
typescript
type ReplaceAll<
S extends string,
From extends string,
To extends string
> = From extends ''
? S
: S extends `${infer L}${From}${infer R}`
? `${L}${To}${ReplaceAll<R, From, To>}`
: S;4.4 字符串转联合类型
typescript
type StringToUnion<S extends string> =
S extends `${infer First}${infer Rest}`
? First | StringToUnion<Rest>
: never;
type Result = StringToUnion<'hello'>; // 'h' | 'e' | 'l' | 'o'4.5 CamelCase 转换
typescript
type CamelCase<S extends string> =
S extends `${infer L}_${infer R}`
? `${Lowercase<L>}${CamelCase<Capitalize<R>>}`
: Lowercase<S>;
type Result = CamelCase<'foo_bar_baz'>; // 'fooBarBaz'五、深度类型操作
5.1 DeepPartial
typescript
type DeepPartial<T> = T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;5.2 DeepReadonly
typescript
type DeepReadonly<T> = T extends Function
? T
: T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;5.3 DeepRequired
typescript
type DeepRequired<T> = T extends object
? { [P in keyof T]-?: DeepRequired<T[P]> }
: T;5.4 路径类型 (Get Nested Property)
typescript
type Get<T, Path extends string> =
Path extends `${infer Key}.${infer Rest}`
? Key extends keyof T
? Get<T[Key], Rest>
: never
: Path extends keyof T
? T[Path]
: never;
// 使用
interface Nested {
a: { b: { c: string } };
}
type Result = Get<Nested, 'a.b.c'>; // string六、数学运算类型
6.1 构造长度为 N 的数组
typescript
type BuildArray<
N extends number,
Arr extends any[] = []
> = Arr['length'] extends N
? Arr
: BuildArray<N, [...Arr, any]>;
type Arr5 = BuildArray<5>; // [any, any, any, any, any]6.2 加法
typescript
type Add<A extends number, B extends number> =
[...BuildArray<A>, ...BuildArray<B>]['length'];
type Sum = Add<3, 4>; // 76.3 减法
typescript
type Subtract<A extends number, B extends number> =
BuildArray<A> extends [...BuildArray<B>, ...infer Rest]
? Rest['length']
: never;
type Diff = Subtract<10, 3>; // 7七、高频面试题
Q1: 实现 PromiseAll 的返回类型
typescript
declare function PromiseAll<T extends any[]>(
values: readonly [...T]
): Promise<{
[K in keyof T]: Awaited<T[K]>;
}>;Q2: 将联合类型转为交叉类型
typescript
type UnionToIntersection<U> =
(U extends any ? (arg: U) => void : never) extends (arg: infer I) => void
? I
: never;
type Result = UnionToIntersection<{ a: 1 } | { b: 2 }>; // { a: 1 } & { b: 2 }Q3: 判断是否为联合类型
typescript
type IsUnion<T, C = T> =
T extends C
? [C] extends [T]
? false
: true
: never;
type A = IsUnion<string | number>; // true
type B = IsUnion<string>; // false