Skip to content

TypeScript 类型系统全景

TypeScript 的类型系统是结构化类型(Structural Typing),不是名义类型。理解这一点是掌握 TS 的关键。

基础类型

ts
// 原始类型
let name: string = 'Alice'
let age: number = 30
let active: boolean = true
let nothing: null = null
let undef: undefined = undefined
let big: bigint = 9007199254740991n
let sym: symbol = Symbol('id')

// 数组
let nums: number[] = [1, 2, 3]
let strs: Array<string> = ['a', 'b']

// 元组(固定长度和类型)
let point: [number, number] = [10, 20]
let entry: [string, number] = ['age', 30]

// any / unknown / never / void
let anything: any = 'hello'  // 关闭类型检查,尽量避免
let safe: unknown = getData() // 使用前必须类型收窄
let fn: () => void            // 函数无返回值
function fail(): never {      // 永远不会正常返回
  throw new Error('fatal')
}

接口 vs 类型别名

ts
// interface:可扩展,适合定义对象形状
interface User {
  id: number
  name: string
  email?: string  // 可选属性
  readonly createdAt: Date  // 只读
}

// 接口扩展
interface Admin extends User {
  role: 'admin' | 'superadmin'
  permissions: string[]
}

// 接口合并(Declaration Merging)
interface Window {
  myCustomProp: string  // 扩展全局 Window 类型
}

// type alias:更灵活,支持联合、交叉、映射等
type ID = string | number
type Point = { x: number; y: number }
type Nullable<T> = T | null

// 交叉类型(合并多个类型)
type AdminUser = User & { role: string }

// interface vs type 选择:
// - 对象形状 → interface(支持合并,更语义化)
// - 联合/交叉/映射类型 → type

联合类型与类型收窄

ts
type StringOrNumber = string | number

function format(value: StringOrNumber): string {
  // typeof 收窄
  if (typeof value === 'string') {
    return value.toUpperCase()  // 这里 value 是 string
  }
  return value.toFixed(2)  // 这里 value 是 number
}

// 判别联合(Discriminated Union)— 最常用的模式
type Shape =
  | { kind: 'circle';    radius: number }
  | { kind: 'rectangle'; width: number; height: number }
  | { kind: 'triangle';  base: number; height: number }

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'rectangle':
      return shape.width * shape.height
    case 'triangle':
      return 0.5 * shape.base * shape.height
    default:
      // 穷举检查:如果新增了 kind 但没处理,这里会报错
      const _exhaustive: never = shape
      throw new Error(`未处理的形状: ${_exhaustive}`)
  }
}

// in 操作符收窄
type Cat = { meow(): void }
type Dog = { bark(): void }

function makeSound(animal: Cat | Dog) {
  if ('meow' in animal) {
    animal.meow()  // Cat
  } else {
    animal.bark()  // Dog
  }
}

// 自定义类型守卫
function isUser(obj: unknown): obj is User {
  return typeof obj === 'object' && obj !== null && 'id' in obj && 'name' in obj
}

泛型基础

ts
// 泛型函数
function identity<T>(value: T): T {
  return value
}

// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const user = { id: 1, name: 'Alice' }
getProperty(user, 'name')  // string
getProperty(user, 'id')    // number
// getProperty(user, 'age') // ❌ 编译错误

// 泛型接口
interface Repository<T> {
  findById(id: number): Promise<T>
  findAll(): Promise<T[]>
  save(entity: T): Promise<T>
  delete(id: number): Promise<void>
}

// 泛型类
class Stack<T> {
  private items: T[] = []

  push(item: T): void { this.items.push(item) }
  pop(): T | undefined { return this.items.pop() }
  peek(): T | undefined { return this.items[this.items.length - 1] }
  get size(): number { return this.items.length }
}

const stack = new Stack<number>()
stack.push(1)
stack.push(2)
stack.pop()  // 2

工具类型

ts
interface User {
  id: number
  name: string
  email: string
  password: string
  createdAt: Date
}

// Partial<T>:所有属性变可选
type UserUpdate = Partial<User>

// Required<T>:所有属性变必填
type RequiredUser = Required<Partial<User>>

// Pick<T, K>:选取部分属性
type UserPreview = Pick<User, 'id' | 'name'>

// Omit<T, K>:排除部分属性
type PublicUser = Omit<User, 'password'>

// Record<K, V>:键值映射
type UserMap = Record<string, User>
type StatusMap = Record<'active' | 'inactive' | 'banned', number>

// Readonly<T>:所有属性只读
type ImmutableUser = Readonly<User>

// ReturnType<T>:获取函数返回类型
function fetchUser(): Promise<User> { /* ... */ }
type FetchResult = ReturnType<typeof fetchUser>  // Promise<User>

// Parameters<T>:获取函数参数类型
type FetchParams = Parameters<typeof fetchUser>  // []

// NonNullable<T>:排除 null 和 undefined
type SafeString = NonNullable<string | null | undefined>  // string

// Awaited<T>:解包 Promise(ES2022)
type UserData = Awaited<Promise<User>>  // User

模板字面量类型

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

// 实际应用:类型安全的事件系统
type CSSProperty = 'margin' | 'padding' | 'border'
type CSSDirection = 'Top' | 'Right' | 'Bottom' | 'Left'
type CSSLonghand = `${CSSProperty}${CSSDirection}`
// 'marginTop' | 'marginRight' | ... | 'borderLeft'

// API 路径类型
type ApiVersion = 'v1' | 'v2'
type Resource = 'users' | 'posts' | 'comments'
type ApiPath = `/api/${ApiVersion}/${Resource}`
// '/api/v1/users' | '/api/v1/posts' | ...

satisfies 操作符(TS 4.9)

ts
type Colors = 'red' | 'green' | 'blue'
type ColorMap = Record<Colors, string | [number, number, number]>

// satisfies:验证类型但保留字面量类型推断
const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255]
} satisfies ColorMap

// palette.red 的类型是 [number, number, number](而非 string | [number, number, number])
palette.red.map(v => v * 2)  // ✓ 不报错
palette.green.toUpperCase()  // ✓ 不报错

总结

  • TS 是结构化类型系统,形状匹配即可赋值
  • 优先用判别联合(Discriminated Union)处理多态
  • 工具类型(Partial/Pick/Omit/Record)是日常开发必备
  • unknownany 更安全,使用前必须收窄
  • satisfies 在需要类型验证同时保留推断时非常有用

系统学习 Web 前端生态,深入底层架构