TypeScript 高级类型体操指北

从条件类型、模板字面量到类型推断,掌握让同事眼前一亮的 TS 工具类型写法。

2 分钟阅读288 字

一、概述

当基础类型不再够用时,TypeScript 提供的"类型层级"允许我们像写代码一样构造复杂类型。本文通过真实业务场景,带你从"会写工具类型"走向"能设计类型库"。

二、背景

类型体操(Type Gymnastics)不是炫技,而是把运行时约束前移到编译期的能力。当我们能为 API 定义精确的输入输出类型时,IDE 的自动补全、错误提示、重构能力都会被同步增强。

三、实战片段

3.1 条件类型与 infer

TS
type Awaited<T> = T extends Promise<infer U> ? U : T

type T0 = Awaited<Promise<string>> // string
type T1 = Awaited<number>          // number

3.2 模板字面量类型

TS
type EventName<T extends string> = `on${Capitalize<T>}`

type T0 = EventName<'click'> // "onClick"

3.3 递归类型:DeepReadonly

TS
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}

interface State {
  user: { name: string; tags: string[] }
}
const s: DeepReadonly<State> = { user: { name: 'a', tags: ['x'] } }
s.user.tags.push('y') // ❌ 类型错误

3.4 工具类型组合

TS
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>

interface Config { id?: string; name: string; debug?: boolean }
type T0 = RequiredBy<Config, 'id' | 'debug'> // id 和 debug 必填

四、最佳实践

  • 优先使用工具类型组合 而不是新造轮子。
  • 命名即文档:ApiResponse<T, K extends 'ok' | 'err'> 优于 R
  • 避免无限递归:在递归类型中加上 unknown[] / 深度阈值守卫。
  • 善用 satisfies:在保留字面量推断的同时校验结构。
TS
const config = {
  api: 'https://api.example.com',
  timeout: 5000
} satisfies Record<string, string | number>

五、总结

类型体操的核心是用类型表达约束,让编译器成为我们的第一位 QA。练手的最佳方式是读 TypeScript Handbook + 看 type-challenges 仓库的 PR。


文章 slug: typescript-advanced-types 最后更新: 2026-06-14

版权声明 · CC BY-NC-ND 4.0

署名-非商业性使用-禁止演绎 4.0 国际

版权归属

本作品著作权归 窦长友 所有, 首次发布于 ,受相关知识产权法律法规保护。

授权范围
  • 可自由分享 — 在任何媒介以任何形式复制、转载本文
  • 不得用于商业目的 — 未经书面授权禁止商用
  • 禁止演绎修改 — 不得改编、转换或以本文为基础再创作
署名要求

转载或引用时须明确标注作者姓名原文出处及本许可协议链接。 不得以任何方式暗示或声称作者为您的使用背书。

评论

由 GitHub Discussions 驱动