Kodokon kodokon.com

泛型:简单的泛型函数与类型

编写可复用的函数和类型,在每次调用时都完整保留类型的精确性。

9 分钟 · 3 题

在 Kodokon 中打开本课

你其实已经在不知不觉中使用泛型了:string[]Promise<number>……是时候编写你自己的泛型了。泛型引入了一个类型参数,一个在每次使用时被替换为具体类型的变量。目标是:逻辑只写一次,而不牺牲类型的精确性。

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

const n = first([10, 20, 30]);
const s = first(["a", "b"]);
T 被推断出来:n 是 number | undefined,s 是 string | undefined

注意你在调用处没有指定任何东西:TypeScript 从实参中推断出了 T。一个不加约束的泛型可以接受任何东西;然而函数往往需要对其数据有最起码的保证。extends 关键字为 T 施加了一个最小形状。

TYPESCRIPT
interface HasId {
  id: number;
}

function findById<T extends HasId>(
  items: T[],
  id: number
): T | undefined {
  return items.find((item) => item.id === id);
}
约束:T 至少必须有一个数字类型的 id

类型本身也可以是泛型的。这在网络代码中是家常便饭:响应的结构是稳定的,只有 data 的内容会变化。于是单一的一个类型就能描述你所有的 API 响应。

TYPESCRIPT
interface ApiResponse<T> {
  ok: boolean;
  data: T;
}

type UserResponse = ApiResponse<{ name: string }>;

const res: UserResponse = {
  ok: true,
  data: { name: "Ada" },
};
泛型类型将 data 的内容参数化

知识检测

确认你已牢记本课的重点内容。

  1. 在 function first<T>(items: T[]): T | undefined 中,T 代表什么?
    • 一个一劳永逸定义好的具体类型
    • 一个在每次调用时被替换的类型参数
    • unknown 类型
    • any 的简写
  2. 约束 <T extends HasId> 保证了什么?
    • T 恰好就是 HasId 类型
    • T 至少具备 HasId 的那些属性
    • T 继承自 HasId 类
  3. 你在不指定类型的情况下调用 first([10, 20, 30]),T 是什么?
    • any
    • unknown
    • number
    • 10 | 20 | 30