Skip to content

TypeScript 类型体操

一、类型体操基础

1.1 什么是类型体操

类型体操是指利用 TypeScript 类型系统的高级特性(泛型、条件类型、映射类型、模板字符串类型等)来实现复杂的类型转换和类型计算。

1.2 核心技巧

  1. 递归类型:处理嵌套结构
  2. 条件类型 + infer:类型推断与提取
  3. 映射类型:批量转换属性
  4. 模板字符串类型:字符串操作

二、经典类型挑战

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>; // 3

3.2 获取数组最后一个元素类型

typescript
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;

type tail = Last<[3, 2, 1]>; // 1

3.3 获取数组长度

typescript
type Length<T extends readonly any[]> = T['length'];

type len = Length<[1, 2, 3]>; // 3

3.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>; // 7

6.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

前端面试知识库