← 返回题目列表

泛型约束和泛型默认参数有什么作用?

中等 第 23 / 32 题 更新于 2026/07/29
TypeScript泛型类型约束

简化版

泛型约束用 extends 限制类型参数必须满足某种结构,让函数内部可以安全访问特定属性。泛型默认参数用于调用方不传类型参数时提供默认类型。

它们都用于提高泛型 API 的可用性:约束保证安全,默认参数减少使用成本。

详细版

function getId<T extends { id: string }>(value: T) {
  return value.id;
}

没有约束时,T 可以是任何类型,不能直接访问 id

默认参数:

type ApiResponse<T = unknown> = {
  data: T;
  code: number;
};

type AnyResponse = ApiResponse;

默认参数常用于组件 props、请求响应、工具类型,让简单场景少写类型参数,复杂场景仍可覆盖。

完整版教学

一、泛型不是越宽越好

完全自由的 T 表示任何类型。

function read<T>(value: T) {
  // value.id; // 不能访问
}

如果函数逻辑需要 id,就要把这个要求写进约束。

泛型约束是在告诉编译器:我不是要任何 T,我要满足某些能力的 T。

二、extends 表示结构约束

function pickId<T extends { id: string | number }>(item: T) {
  return item.id;
}

只要传入对象有兼容的 id 字段即可。

pickId({ id: 1, name: "Ada" });

这体现了 TypeScript 的结构化类型系统。

三、keyof 约束常用于安全取值

function get<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

K extends keyof T 保证 key 一定是 obj 的键。

const user = { id: 1, name: "Ada" };
const name = get(user, "name");

四、默认参数降低使用成本

type Result<T = unknown, E = Error> = {
  data?: T;
  error?: E;
};

简单场景:

type R1 = Result;

复杂场景:

type R2 = Result<User, ApiError>;

五、默认参数可以依赖前面的参数

type Box<T, U = T[]> = {
  value: T;
  list: U;
};

泛型默认参数从左到右解析,后面的默认值可以引用前面的类型参数。

写法含义
Box<string>U 默认为 string[]
Box<string, Set<string>>手动指定 U

六、约束不是转换

function f<T extends string | number>(value: T) {
  return value;
}

extends 只是限制传入类型范围,不会把值转换成 string 或 number。运行时仍然是原值。

如果需要运行时转换,必须写真实代码。

七、常见误区与追问

  • 误区:泛型约束会改变运行时数据。 它只影响类型检查,不做转换。
  • 误区:T extends { id: string } 要求对象只能有 id。 结构兼容允许额外属性。
  • 误区:默认泛型参数会降低类型安全。 合理默认能减少样板,同时保留覆盖能力。
  • 误区:所有泛型都应该有默认值。 关键类型参数若必须显式表达,就不应随便默认。
  • 追问:K extends keyof T 有什么用? 保证 key 是对象真实键,并返回对应属性类型。
  • 追问:默认类型参数能引用其他参数吗? 后面的默认参数可以引用前面的类型参数。

八、加强记忆

记忆句:

constraint -> what T must have
default    -> what T becomes if omitted

回答时用 get<T, K extends keyof T> 说明约束价值,再用 ApiResponse<T = unknown> 说明默认参数的 API 友好性。