Skip to content

TypeScript 类型体操实用技巧

整理日常开发中遇到的 TypeScript 高级类型用法,让类型系统为你服务。

为什么要学类型体操

TypeScript 的类型系统是图灵完备的,虽然日常开发不需要写太复杂的类型,但掌握一些常用技巧可以:

  • 减少 any 的使用,提升代码安全性
  • 让 IDE 提供更精准的智能提示
  • 在编译期就发现潜在错误

泛型约束

泛型让我们编写可复用的类型,extends 关键字可以约束泛型的范围:

typescript
// 约束 T 必须有 length 属性
function logLength<T extends { length: number }>(value: T): void {
  console.log(value.length)
}

logLength('hello')    // ✅ 字符串有 length
logLength([1, 2, 3])  // ✅ 数组有 length
logLength(123)        // ❌ 数字没有 length

实际应用场景

typescript
// 从对象中安全地获取属性值
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const user = { name: 'Alice', age: 25 }
getProperty(user, 'name')  // ✅ 返回 string
getProperty(user, 'email') // ❌ 'email' 不存在于 user 类型

条件类型

条件类型根据条件选择不同的类型,语法类似三元表达式:

typescript
type IsString<T> = T extends string ? 'yes' : 'no'

type A = IsString<string>  // 'yes'
type B = IsString<number>  // 'no'

实用案例:提取函数返回类型

typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never

function getUser() {
  return { name: 'Alice', age: 25 }
}

type User = ReturnType<typeof getUser>
// { name: string; age: number }

映射类型

映射类型可以基于已有类型创建新类型:

typescript
// 将所有属性变为可选(内置的 Partial 就是这么实现的)
type MyPartial<T> = {
  [K in keyof T]?: T[K]
}

// 将所有属性变为只读
type MyReadonly<T> = {
  readonly [K in keyof T]: T[K]
}

// 实用:将对象所有属性变为字符串类型
type ToString<T> = {
  [K in keyof T]: string
}

工具类型实战

提取对象中的函数属性

typescript
type FunctionKeys<T> = {
  [K in keyof T]: T[K] extends Function ? K : never
}[keyof T]

interface User {
  name: string
  greet(): void
  age: number
  farewell(): void
}

type Methods = FunctionKeys<User>  // 'greet' | 'farewell'

深度只读

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

总结

类型体操不需要一次学完,建议在实际项目中遇到问题时逐步深入。最重要的原则:类型是为开发服务的,不要为了炫技而过度设计类型