Skip to content

TypeScript 7.2 新特性:模板字面量类型的进阶用法

TypeScript 的模板字面量类型从 4.1 引入至今,已经成为类型体操的核心能力之一。7.2 版本在此基础上增加了更强大的推断能力和内置字符串操作工具,让复杂的字符串类型推断变得更加直观。

基础回顾与 7.2 增强

模板字面量类型的核心是 ${T} 语法,它允许将字符串字面量类型嵌入模板中:

typescript
type EventName = 'click' | 'focus' | 'blur'
type HandlerName = `on${Capitalize<EventName>}`
// 'onClick' | 'onFocus' | 'onBlur'

7.2 新增了 Uncapitalize<T> 工具类型,与 Capitalize 形成对称:

typescript
type MethodName = 'getUserData' | 'setUserData'
type RawName = Uncapitalize<MethodName>
// 'getUserData' | 'setUserData' — 已经是小写开头

type ClassName = 'ButtonPrimary' | 'CardSecondary'
type cssClass = Uncapitalize<ClassName>
// 'buttonPrimary' | 'cardSecondary'

类型推断的进阶模式

7.2 最重要的改进是模板字面量推断能力的增强。现在可以在更复杂的模式中提取子字符串:

typescript
type ExtractRouteParams<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractRouteParams<Rest>
    : T extends `${string}:${infer Param}`
      ? Param
      : never

type Params = ExtractRouteParams<'/users/:id/posts/:postId'>
// 'id' | 'postId'

type SingleParam = ExtractRouteParams<'/api/:version'>
// 'version'

这个模式在构建类型安全的路由系统时非常有用。结合 infer 关键字,可以实现更精细的字符串解析:

typescript
type ParseQueryString<T extends string> =
  T extends `${infer Key}=${infer Value}&${infer Rest}`
    ? { [K in Key]: Value } & ParseQueryString<Rest>
    : T extends `${infer Key}=${infer Value}`
      ? { [K in Key]: Value }
      : {}

type Query = ParseQueryString<'page=1&limit=20&sort=date'>
// { page: '1' } & { limit: '20' } & { sort: 'date' }

实际应用场景

API 路径类型安全

在大型项目中,API 路径的类型安全可以显著减少运行时错误:

typescript
type ApiVersion = 'v1' | 'v2' | 'v3'
type Resource = 'users' | 'posts' | 'comments'
type Action = 'list' | 'create' | 'update' | 'delete'

type ApiPath = `/${ApiVersion}/${Resource}/${Action}`
// '/v1/users/list' | '/v1/users/create' | ... (共 36 种组合)

function apiCall<T extends ApiPath>(path: T): Promise<Response> {
  return fetch(`/api${path}`)
}

// 类型安全的调用
apiCall('/v2/posts/list')  // ✅
apiCall('/v1/users/delete')  // ✅
apiCall('/v4/items/get')  // ❌ 类型错误

CSS 变量类型推断

结合 CSS 自定义属性,可以实现主题变量的类型安全:

typescript
type ThemeToken = 'primary' | 'secondary' | 'accent' | 'background' | 'text'
type TokenShade = '50' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'

type CssVariable = `--color-${ThemeToken}-${TokenShade}`

// 自动补全所有有效的 CSS 变量
const theme = {
  '--color-primary-500': '#3b82f6',
  '--color-secondary-100': '#e0e7ff',
  // ...
} as const satisfies Record<CssVariable, string>

递归模板字面量类型

7.2 优化了递归模板字面量类型的性能,让深层嵌套的类型推断更加高效:

typescript
type Split<S extends string, D extends string> =
  S extends `${infer Head}${D}${infer Tail}`
    ? [Head, ...Split<Tail, D>]
    : [S]

type Result = Split<'a.b.c.d', '.'>
// ['a', 'b', 'c', 'd']

// 7.2 优化:即使在深层嵌套中也能保持良好的性能
type DeepPath = Split<'a.b.c.d.e.f.g.h.i.j.k', '.'>
// ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']

与条件类型的配合

模板字面量类型与条件类型的配合可以实现复杂的类型转换:

typescript
type Camelize<T extends string> =
  T extends `${infer Head}_${infer Tail}`
    ? `${Head}${Camelize<Capitalize<Tail>>}`
    : T

type SnakeToCamel<T extends string> = Camelize<T>

type Result1 = SnakeToCamel<'user_name'>  // 'userName'
type Result2 = SnakeToCamel<'get_user_data'>  // 'getUserData'
type Result3 = SnakeToCamel<'is_active'>  // 'isActive'

// 批量转换对象键名
type CamelizeKeys<T extends Record<string, any>> = {
  [K in keyof T as Camelize<K & string>]: T[K]
}

interface RawUser {
  user_name: string
  created_at: string
  is_active: boolean
}

type User = CamelizeKeys<RawUser>
// { userName: string; createdAt: string; isActive: boolean }

性能注意事项

虽然 7.2 优化了递归类型性能,但在实际项目中仍需注意:

  1. 避免过深的递归:超过 10 层的递归模板字面量类型可能导致编译时间显著增加
  2. 合理使用 never 短路:在递归类型中提前返回 never 可以避免无效计算
  3. 缓存复杂类型:将频繁使用的复杂类型提取为独立的类型别名
typescript
// 好的做法:提取复杂类型
type ValidRoute = ExtractRouteParams<`${string}:${string}`>
type RouteHandler<T extends string> = (params: Record<ValidRoute, string>) => void

// 避免每次重复计算
// ❌ 不好的做法
type Bad = ExtractRouteParams<'/:a/:b'> & ExtractRouteParams<'/:c/:d'>

// ✅ 好的做法
type Params1 = ExtractRouteParams<'/:a/:b'>
type Params2 = ExtractRouteParams<'/:c/:d'>
type Combined = Params1 & Params2

小结

TypeScript 7.2 的模板字面量类型增强让字符串操作的类型推断更加强大和直观。从 API 路径类型安全到 CSS 变量推断,从路由参数解析到对象键名转换,这些能力在大型项目中能显著提升开发体验和代码可靠性。掌握这些模式的关键是理解递归推断的思维方式——把字符串当作可以被拆解和重组的数据结构。

MIT Licensed