← 返回题目列表

TypeScript 泛型是什么?为什么要用泛型?

高频 中等 第 10 / 32 题 更新于 2026/07/27
TypeScript泛型类型复用

简化版

泛型是类型层面的参数化,让函数、接口、类在保持类型安全的同时复用逻辑。它能把输入类型和输出类型关联起来,避免用 any 丢失类型信息。

详细版

示例:

function identity<T>(value: T): T {
  return value;
}

const n = identity(1); // number
const s = identity('a'); // string

如果用 any:

function identity(value: any): any {
  return value;
}

返回值类型也变成 any,调用方失去提示。泛型保留了类型关系。

泛型还能加约束:

function getLength<T extends { length: number }>(value: T) {
  return value.length;
}

完整版教学

一、泛型解决“类型也要复用”的问题

普通函数复用值逻辑,泛型复用类型逻辑。比如数组取第一项,输入是 number[] 返回 number,输入是 User[] 返回 User。

function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

没有泛型就很难表达这种关系。

二、泛型参数由调用推断

很多时候不需要手写类型参数:

first([1, 2, 3]);

TS 会推断 T 是 number。只有推断不出来或需要指定更宽类型时,才显式写:

first<string | number>([1, 'a']);

三、泛型约束

没有约束的 T 可以是任何类型,所以不能随便访问属性:

function f<T>(x: T) {
  // x.length; // 报错
}

extends 约束后,TS 才知道它至少有某些能力。

四、常见应用

泛型常用于接口响应:

interface ApiResponse<T> {
  code: number;
  data: T;
}

也用于组件 props、表格列配置、状态管理、工具函数等。它的核心价值是让复用代码不丢类型。

五、面试追问与工程落地

泛型常见追问是“泛型和 any 有什么本质区别”。any 是放弃类型关系,泛型是保留类型关系。function first<T>(arr: T[]): T 能保证传 number[] 返回 number;any 只能告诉你“随便是什么”,调用方失去约束和提示。

还会追问泛型默认值和多泛型。比如组件 props、请求函数中常写 TData = unknown,让调用方可以不传类型参数,也能有安全默认值。多个泛型之间可以互相约束,如 K extends keyof T,表达 key 和对象的关系。

工程里泛型不要为了炫技过度抽象。如果一个函数没有真实类型关系,只是把所有地方都写成 T,会让调用更复杂。好泛型应该减少重复、增强约束;坏泛型只是把简单问题变难。

六、从推断候选到约束检查理解泛型调用

泛型调用不是简单地把实参类型复制到 T。编译器会从参数、上下文返回类型等位置收集候选,再选择满足约束的类型;显式类型参数则会跳过部分推断,但仍必须通过 extends 约束。泛型默认值只在无法或无需提供类型参数时给出后备,不会覆盖已经推断出的候选。

function get<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}
const user = { id: 1, name: 'Ada' }
const id = get(user, 'id')     // number
const name = get(user, 'name') // string
// get(user, 'age')            // 报错
写法类型关系调用结果
(value: any) => any输入输出断开返回 any
<T>(value: T) => T输入输出同一类型参数保留实参类型
<T extends X>(value: T)保留具体类型且保证至少满足 X可安全使用 X 的能力
<T = unknown>()没有候选时提供默认值安全后备类型

上例对象有 2 个键,因此 K 只能从 'id' | 'name' 中选择;选择 'id' 后,索引访问类型 T[K] 精确得到 number。若把 key 只写成 string,这种键和值之间的关联就丢失了。

好泛型必须表达真实关系:同一个 T 至少应连接两个位置,或者用约束把两个类型参数关联起来;孤立的泛型往往只是增加阅读成本。

七、常见误区与追问

  • 误区:泛型就是类型安全的 any。 any 放弃关系检查,泛型通过同一类型参数保存输入、输出或成员之间的关系。
  • 误区:使用泛型时总要显式写尖括号。 大多数函数调用可以从实参或上下文推断,显式参数只在推断不足或需要主动拓宽时使用。
  • 误区:T extends X 会把 T 固定成 X。 它只规定 T 至少满足 X,返回值仍可保留调用方传入的更具体类型。
  • 追问:为什么泛型约束常写 K extends keyof T? 它保证 K 是 T 的合法键,使 T[K] 能安全表达对应属性值类型。
  • 追问:泛型默认值什么时候生效? 当类型参数可选且没有推断候选或调用方没有提供时使用,前面的必填类型参数也要满足声明顺序规则。
  • 追问:泛型在运行时还存在吗? 普通 TypeScript 泛型会被擦除,不能在运行时通过 Tinstanceof 或创建 new T()
  • 追问:什么时候不该使用泛型? 当类型参数只出现一次、没有连接任何类型关系时,直接写具体类型或联合类型通常更清楚。

八、加强记忆

泛型就是“把类型当参数传”。它不是为了复杂,而是为了表达输入和输出的类型关系:逻辑复用,类型不丢。

最后检查类型参数是否连接了至少两个位置;能保留或约束关系才是泛型存在的理由。