TypeScript 泛型

在 TypeScript 的世界里,如果说"接口(Interface)"定义了数据的形状,那么"泛型(Generics)"就是赋予代码灵魂的灵活性。

一、 为什么需要泛型?

想象一下,你需要写一个函数,它的功能很简单:返回传入的参数。

typescript 复制代码
// 初级写法:只支持 number
function identity(arg: number): number {
    return arg;
}

// 进阶写法:为了支持所有类型,用了 any
function identityAny(arg: any): any {
    return arg;
}

问题出在哪?

使用 any 会导致类型信息的彻底丢失 。如果你传入一个字符串,TS 此时只知道返回值是 any,而失去了它是 string 的上下文。

泛型(Generics)登场:

泛型就像是一个类型变量 ,它在定义时不确定类型,而在调用时捕获类型。

typescript 复制代码
function identity<T>(arg: T): T {
    return arg;
}

// 此时 output 的类型被推断为 string
const output = identity("Hello Generics"); 

二、 泛型的四大核心用法

1. 泛型函数

除了简单的返回,泛型在处理集合(如数组)时最为常用。

typescript 复制代码
function getFirstElement<T>(list: T[]): T | undefined {
    return list[0];
}

2. 泛型接口

在处理后端 API 返回值时,这是最经典的应用场景。

typescript 复制代码
interface ApiResponse<Data> {
    code: number;
    message: string;
    data: Data; // 这里的 Data 是动态的
}

interface User {
    id: number;
    name: string;
}

const userResponse: ApiResponse<User> = {
    code: 200,
    message: "success",
    data: { id: 1, name: "Alice" }
};

3. 泛型类

构建通用的数据结构,如堆栈(Stack)或队列。

typescript 复制代码
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

4. 泛型约束 (Generic Constraints)

有时候你不想让 T 是任何类型,而是希望它具备某些属性。

typescript 复制代码
interface Lengthwise {
    length: number;
}

// 限制 T 必须拥有 length 属性
function logLength<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

logLength("abc"); // OK
logLength([1, 2]); // OK
// logLength(123); // Error: number 没有 length 属性

三、 进阶

泛型之所以强大,是因为它可以配合 TS 的内置操作符进行"逻辑计算"。

1. 结合 keyof 确保对象属性安全

typescript 复制代码
function getProperty<T, K extends keyof T>(obj: T, key: K) {
    return obj[key];
}

const user = { name: "Bob", age: 30 };
getProperty(user, "name"); // OK
// getProperty(user, "gender"); // Error: 'gender' 不在 user 的键名中

2. 默认泛型参数

就像 ES6 函数参数可以有默认值一样,泛型也可以。

typescript 复制代码
interface MyConfig<T = string> {
    value: T;
}

const config: MyConfig = { value: "default" }; // T 默认为 string

3. 条件类型与 infer

这是 TS 泛型的高阶玩法,用于在类型层级进行条件判断。

typescript 复制代码
// 如果 T 是数组,则取其元素类型,否则直接返回 T
type Flatten<T> = T extends any[] ? T[number] : T;

type Str = Flatten<string[]>; // string
type Num = Flatten<number>;   // number

四、 总结

  1. 命名规范 :简单场景使用 T, U, V;复杂场景建议使用语义化命名(如 TUser, TResponse)。
  2. 避免滥用:如果一个函数不涉及类型之间的关联,或者不返回基于输入类型的结构,可能不需要泛型。
  3. 约束先行 :尽量通过 extends 限制泛型的范围,而不是让它无限制地接收任何类型。
  4. 利用推导 :TS 的类型推导非常强大,如果能自动推导出泛型,就不要显式写出 <Type>。
相关推荐
quantdash_cc几秒前
如何检查滚动收益率计算中的窗口长度不足?——从数学定义到 Pandas 实现
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
陈随易3 分钟前
副业搞钱点子之赛事与活动
前端·后端·程序员
电梯界知识分子5 分钟前
景德镇老房重新设计阶段规划楼梯与电梯,1.7m×1.6m空间装400kg观光电梯,棕色框架茶色玻璃适配中式装修
大数据·服务器·前端·网络·家用电梯
Shell运维手记14 分钟前
Shell 脚本入门课堂讲解
前端·chrome
衍生星球17 分钟前
【Java 语言】— 方法(方法定义、调用、参数、返回值、方法重载)
java·开发语言·方法
Profile排查笔记18 分钟前
有哪些开源指纹浏览器项目?先核对客户端、内核与许可证
前端·人工智能·后端·自动化
CodeGL23 分钟前
16、Python Web开发
开发语言·前端·python
喵了几个咪25 分钟前
GoWind UBA 拆解:把 Doris UNIQUE KEY 翻译成 ClickHouse——读数正确性对齐实录
开发语言·后端·clickhouse·微服务·golang·gowind
FRing0330 分钟前
JavaScript入门到精通二十九(内置构造函数(Object、Array))
开发语言·前端·javascript
syagain_zsx33 分钟前
ODB 与 MySQL:C++ 里的 ORM 实践(类即表、事务铁律与查询封装)
开发语言·数据库·c++·mysql·工具库·1024程序员节