Skip to content

TypeScript 泛型 详解

你最常使用的泛型模式,按用途分组:声明类型参数、约束它、给它默认值、用条件类型分支、用 infer 捕获、以及对一个形状做映射。每一行将模式与一行示例配对。

泛型就是一个类型参数:一个由调用方选择的类型的占位符,让同一个函数或类型适用于多种形状而不损失精度。把 <T> 读作“对于某个类型 T”。它的威力来自约束 T、对它分支、以及对它映射。下面的模式就是常用的词汇表。

参考表格 · 28 条目
28 of 28 rows
声明
由调用方选择的类型参数。function first<T>(xs: T[]): T
多个相互独立的类型参数。function pair<T, U>(a: T, b: U): [T, U]
与第一个参数的键绑定的第二个参数。function get<T, K extends keyof T>(o: T, k: K): T[K]
一个类,其字段和方法上带有类型参数。class Box<T> { value: T }
一个带类型参数的对象形状。interface Result<T> { data: T }
箭头函数上的类型参数;尾随逗号防止 .tsx 文件把它读成 JSX。const first = <T,>(xs: T[]): T => xs[0]
约束
要求 T 可赋值给 Type。function len<T extends { length: number }>(x: T)
把 T 约束为另一个类型的键。type Keys<O> = keyof O
把 T 约束为可构造的类。function make<C extends new () => C>(c: C): C
要求 T 同时满足多个约束。function log<T extends Error & { code: number }>(e: T)
约束为任意类型;让参数保持泛型而不收窄它。type Wrap<T> = T extends unknown ? { v: T } : never
默认值与 infer
当调用方省略时给 T 一个默认值。function parse<T = string>(): T
在条件类型内部捕获一个类型。type El<T> = T extends Array<infer I> ? I : never
把嵌套的 Promise 解包为它的内部类型。type Data = Awaited<Promise<User>>
捕获模板字面量类型的一部分。type Path<S> = S extends `/api/${infer R}` ? R : never
一次性检查整个联合类型;裸 T 每次只推断一个成员。type El<T> = [T] extends [Array<infer I>] ? I : never
条件与映射
根据 T 是否可赋值给 U 来选择一个类型。type IsStr<T> = T extends string ? true : false
通过变换 T 的每个键来构建新的对象类型。type Frozen<T> = { [K in keyof T]: Readonly<T[K]> }
键为 K、值为 V 的对象。type Scores = Record<string, number>
由上述模式派生的内置泛型。type Preview = Pick<User, "id" | "name">
extends 之前的裸 T 会对联合的每个成员各执行一次,并把结果联合起来。ToArray<'a' | 'b'> = 'a'[] | 'b'[]
在映射键上添加或移除修饰符。type Req<T> = { [K in keyof T]-?: T[K] }
把一个运行时值的键表示为类型。const opts = { a: 1 }; type K = keyof typeof opts
从其他字符串类型构建字符串类型。type Route = `/users/${string}`
把元组类型首尾相接。type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
定义引用自身的类型。type Json = string | number | Json[] | { [k: string]: Json }
用类型检查一个值,并保留它更窄的字面量类型。const c = { dark: "#000" } satisfies Record<string, string>
保留调用方的字面量类型而不加宽它(TS 5.0)。function first<const T>(xs: readonly T[]): T