泛型:T、extends 与约束
编写泛型函数和接口,使用 extends 约束类型参数,并使用泛型默认值构建灵活的 API。
泛型:T、extends 与约束 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。
什么是泛型?
泛型让您可以编写适用于多种类型的函数、类和接口,同时保持类型安全。您无需为每种类型分别编写函数,而是编写一个由类型变量参数化的泛型函数。
一个简单的泛型函数
类型参数 T 在尖括号中声明。TypeScript 会根据参数类型推断出 T,因此调用位置无需显式注解。
function identity<T>(value: T): T {
return value;
}
identity(42); // T is inferred as number, returns number
identity('hello'); // T inferred as string
identity<boolean>(true); // explicit泛型接口与类型
使用泛型参数化接口和类型,以描述可复用的数据结构。
interface Box<T> {
value: T;
label?: string;
}
const numBox: Box<number> = { value: 42 };
const strBox: Box<string> = { value: 'hello', label: 'greeting' };extends——约束泛型类型
使用 T extends SomeType 来限制 T 可以表示的类型。该函数只接受满足此约束的类型。
function getLength<T extends { length: number }>(item: T): number {
return item.length; // safe: T is guaranteed to have length
}
getLength('hello'); // 5
getLength([1, 2, 3]); // 3
getLength({ length: 7 }); // 7
// getLength(42); // Error: number has no lengthkeyof 与泛型约束
keyof T 会生成 T 的属性键联合类型。它与泛型结合后,可以实现类型安全的属性访问。
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'Alice', age: 30 };
getProperty(user, 'name'); // string
getProperty(user, 'age'); // number
// getProperty(user, 'foo'); // Error泛型默认值
使用 T = DefaultType 为泛型参数提供默认类型。当存在合理的默认值,但调用方可以覆盖它时,就可以采用这种方式。
interface Paginated<T = unknown> {
items: T[];
total: number;
page: number;
}
const rawPage: Paginated = { items: [], total: 0, page: 1 };
const typedPage: Paginated<User> = { items: [], total: 0, page: 1 };多个类型参数
函数可以拥有多个相互独立的类型参数。
function zip<A, B>(arrA: A[], arrB: B[]): Array<[A, B]> {
return arrA.map((a, i) => [a, arrB[i]]);
}
zip([1, 2, 3], ['a', 'b', 'c']);
// [[1,'a'], [2,'b'], [3,'c']]泛型类
类也可以使用泛型。这在栈、队列和可观察存储等数据结构中很常见。
class Stack<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items.at(-1); }
get size() { return this.items.length; }
}
const stack = new Stack<number>();
stack.push(1);
stack.push(2);
stack.pop(); // 2异步泛型函数
异步泛型函数常用于带类型的 API 调用。
async function fetchJson<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<T>;
}
const users = await fetchJson<User[]>('/api/users');
const profile = await fetchJson<UserProfile>('/api/me');在条件类型中使用 infer
条件类型中的 infer 关键字会根据某种模式提取类型。库代码常用它来提取函数的返回类型或数组的元素类型。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type ElementType<T> = T extends Array<infer E> ? E : never;
type Fn = () => { name: string };
type Result = ReturnType<Fn>; // { name: string }何时使用泛型
在以下情况下使用泛型:1)相同逻辑适用于多种类型;2)需要建立输入类型与输出类型之间的关系;3)正在构建可复用的数据结构或工具。不要只是为了使用而使用泛型——如果只有一种使用场景,具体类型会更清晰。
快速检查
约束 T extends { length: number } 能确保什么?
回顾:泛型
泛型可以参数化类型,使一个函数、类或接口适用于多种类型。T extends SomeType 会添加约束。keyof T 会生成属性键联合类型。使用 T = Default 可以设置泛型默认值。infer 可以在条件类型中提取类型。请使用泛型构建可复用的类型化工具和数据结构。
用 AI 导师学习 HTML — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 41
- 课程
- 163
常见问题解答
「泛型:T、extends 与约束」课时是免费的吗?
是的 — 「泛型:T、extends 与约束」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。
「泛型:T、extends 与约束」这节课中我会学到什么?
编写泛型函数和接口,使用 extends 约束类型参数,并使用泛型默认值构建灵活的 API。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Frontend Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「泛型:T、extends 与约束」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Frontend Academy 课中编写并运行代码吗?
能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。