New Chat
Ctrl
K
Chat History
Mobile App

TypeScript 类型体操实现过程详解

ts 类型体操是指什么,给出各种示例
Search
TypeScript 类型体操 应用场景
30 results
“TypeScript 类型体操”是指利用 TypeScript 强大的类型系统,通过条件类型、映射类型、递归、模板字面量类型、infer 关键字等高级特性,对类型进行复杂变换与推导的技术
。
它就像“类型世界里的函数式编程”——输入一个类型,输出一个新的类型,常用于:
  • 从已有类型派生新类型(如 Omit、Pick、Partial)
  • 实现类型级别的逻辑(如判断是否为联合类型)
  • 模拟运行时行为(如字符串处理、数组操作)

✅ 经典类型体操示例(含实现)

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

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

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

2. 首字母大写

TypeScript
Copy
type Capitalize<S extends string> = 
  S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}` : S;

type B = Capitalize<'hello'>; // 'Hello'

3. 深层次的 Readonly(递归)

TypeScript
Copy
type IsPlainObject<T> = T extends (...args: any[]) => any
  ? false
  : T extends object
  ? true
  : false;

type DeepReadonly<T> = {
  readonly [K in keyof T]: IsPlainObject<T[K]> extends true
    ? DeepReadonly<T[K]>
    : T[K];
};

type C = DeepReadonly<{ a: { b: { c: string } } }>;
// { readonly a: { readonly b: { readonly c: string } } }

4. 合并两个对象(冲突时以后者为准)

TypeScript
Copy
type Merge<F, S> = {
  [K in keyof F | keyof S]: K extends keyof S
    ? S[K]
    : K extends keyof F
    ? F[K]
    : never;
};

type D = Merge<{ a: string; b: string }, { b: number; c: boolean }>;
// { a: string; b: number; c: boolean }

5. 判断是否为联合类型

TypeScript
Copy
type IsUnion<T, U = T> = T extends any
  ? [U] extends [T]
    ? false
    : true
  : never;

type E = IsUnion<string | number>; // true
type F = IsUnion<string>;         // false

6. 字符串模板替换

TypeScript
Copy
type Replace<S, From extends string, To extends string> =
  S extends `${infer L}${From}${infer R}` ? `${L}${To}${R}` : S;

type G = Replace<'hello world', 'world', 'ts'>; // 'hello ts'

7. 获取构造函数实例类型

TypeScript
Copy
type Instance<T extends new (...args: any[]) => any> =
  T extends new (...args: any[]) => infer R ? R : never;

class Person {}
type H = Instance<typeof Person>; // Person

🎯 总结一句话

类型体操就是用 TS 的类型系统写“类型级别的函数”,实现类型计算、推导与验证的高级技巧。
如果你想练习,推荐:
  • type-challenges
  • type-gymnastics 项目
需要我帮你拆解某个类型体操的实现过程吗?
下面按顺序拆解 示例 3 ~ 5 的实现过程,逐行解释它们到底在做什么、为什么这样写。

✅ 示例 3:深层次的 Readonly(递归)

TypeScript
Copy
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends (...args: any[]) => any
    ? T[K] // 函数类型不递归
    : T[K] extends object
    ? DeepReadonly<T[K]> // 对象类型递归
    : T[K]; // 原始类型直接返回
};

🔍 拆解过程

  1. [K in keyof T]:遍历 T 的所有键。
  2. 第一层 extends (...args: any[]) => any:判断是否是函数,函数不递归(因为函数是引用类型,递归 readonly 无意义)。
  3. 第二层 extends object:判断是否是对象(排除原始类型如 string、number)。
    • 如果是对象,递归调用 DeepReadonly<T[K]>。
    • 否则直接返回 T[K](原始类型)。
  4. readonly:给所有属性加上 readonly 修饰符。

✅ 示例 4:合并两个对象(冲突时以后者为准)

TypeScript
Copy
type Merge<F, S> = {
  [K in keyof F | keyof S]: K extends keyof S
    ? S[K] // 如果 S 有 K,取 S[K]
    : K extends keyof F
    ? F[K] // 如果 F 有 K 但 S 没有,取 F[K]
    : never; // 兜底(理论上不会触发)
};

🔍 拆解过程

  1. keyof F | keyof S:联合两个对象的所有键。
  2. 条件分发:
    • 如果 K 是 S 的键,优先取 S[K](覆盖)。
    • 否则检查 K 是否属于 F,取 F[K]。
    • never 是兜底(因为 K 一定属于 F 或 S)。
  3. 结果:
    • 合并后的对象包含 F 和 S 的所有键。
    • 冲突时 S 覆盖 F。

✅ 示例 5:判断是否为联合类型

TypeScript
Copy
type IsUnion<T, U = T> = T extends any
  ? [U] extends [T] ? false : true
  : never;

🔍 拆解过程

  1. T extends any:触发 分布式条件类型(Distributive Conditional Types)。
    • 如果 T 是联合类型(如 A | B),会拆成 A 和 B 分别判断。
  2. [U] extends [T]:
    • U 是原始联合类型(如 A | B)。
    • T 是分布式后的单个成员(如 A 或 B)。
    • [A | B] 不能扩展 [A],所以返回 true。
  3. 非联合类型:
    • 如果 T 不是联合类型(如 string),[string] extends [string] 为 true,返回 false。

🎯 核心技巧

  • 分布式条件类型:T extends any 会拆分联合类型。
  • 方括号 []:阻止分布式行为,确保比较的是整体联合类型 vs 单个成员。

📌 一句话总结

Table
Copy
示例核心技巧
示例 3递归 + 条件类型判断对象类型
示例 4联合键遍历 + 条件分发优先级
示例 5分布式条件类型 + 方括号阻止分发
需要我画流程图或再细化某一步吗?
Search
TypeScript 方括号 条件类型 分布式行为 官方文档
30 results
TypeScript 官方文档中明确说明了 “用方括号将 extends 关键字两侧括起来可以阻止分布式条件类型(distributive conditional types)” 的行为。
原文如下(摘自 TypeScript 官方文档
):
Distributed Conditional Types
When conditional types act on a generic type, they become distributive when given a union type.
[...]
Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets.
示例代码:
TypeScript
Copy
// 默认行为:分布式
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;
// => string[] | number[]

// 阻止分布式行为
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
type StrOrNumArr = ToArrayNonDist<string | number>;
// => (string | number)[]
官方文档链接