你将用 extends 约束类型参数,并提供默认值,从而构建符合人体工学的泛型 API。
在 Kodokon 中打开本课无约束的泛型是不透明的:在函数体内部,T 什么都不让你做。extends 子句在类型参数上建立起一份最小契约,从而在保留调用方精确类型的同时,解锁对那些有保证的属性的访问。这正是它与用接口标注参数的根本区别:约束保留了信息,而接口抹去了信息。
function longest<T extends { length: number }>(
a: T,
b: T
): T {
return a.length >= b.length ? a : b;
}
const word = longest("hello", "worldwide");
const list = longest([1, 2], [3, 4, 5]);K extends keyof T 这一组合是日常最划算的模式:它把两个类型参数关联起来,并在静态层面保证某个键确实属于该对象。返回类型 T[K](一种索引访问类型)会精确地跟踪所请求的属性。工具库中的 pick 或 pluck 这类函数正是这样被类型化的。
function getProp<T extends object, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
const config = { port: 8080, host: "localhost" };
const port = getProp(config, "port");
const host = getProp(config, "host");默认值(T = string)让泛型在调用处变为可选:只有当用户想偏离常见情况时,才需要提供类型参数。与约束结合(T extends X = Y)时,它同时定义了上界和隐式行为。默认值必须满足约束,否则声明本身就无法编译。这是 HTTP 客户端、状态存储以及类型化事件发射器的标准模式。
interface ApiResponse<T = unknown> {
status: number;
data: T;
}
async function fetchJson<T = unknown>(
url: string
): Promise<ApiResponse<T>> {
const res = await fetch(url);
return { status: res.status, data: await res.json() };
}
interface Post { id: number; title: string }
const typed = await fetchJson<Post>("/api/posts/1");
const loose = await fetchJson("/api/health");K extends keyof T 保证了什么?longest(a: { length: number }, b: ...),longest<T extends { length: number }> 有什么优势?<T = unknown> 而非 <T = any>?