Skip to content

泛型

泛型是 TypeScript 中非常重要的特性,它允许我们在定义函数、接口或类时,不预先指定具体的类型,而在使用时再指定类型的一种特性。

为什么需要泛型?

  1. 类型复用:避免重复编写多个类型相似的函数
  2. 类型安全:在编译时进行类型检查,保证类型安全
  3. 灵活性:增强代码的可复用性和通用性

基础用法

1. 泛型函数

typescript
// 不使用泛型
function identity1(arg: number): number {
    return arg;
}

// 使用泛型
function identity2<T>(arg: T): T {
    return arg;
}

// 使用示例
const num = identity2<number>(123);    // 明确指定类型
const str = identity2("hello");        // 类型推断

2. 泛型接口

typescript
interface GenericIdentity<T> {
    (arg: T): T;
    value: T;
}

// 使用示例
const myIdentity: GenericIdentity<number> = {
    (arg: number): number { return arg },
    value: 123
};

3. 泛型类

typescript
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
    
    constructor(zero: T, addFn: (x: T, y: T) => T) {
        this.zeroValue = zero;
        this.add = addFn;
    }
}

// 使用示例
const myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);

泛型约束

有时我们需要限制泛型可以接受的类型范围:

typescript
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // 现在我们知道arg一定有length属性
    return arg;
}

// 正确
loggingIdentity("hello");
loggingIdentity([1, 2, 3]);

// 错误
// loggingIdentity(3);  // 数字没有length属性

多个类型参数

泛型可以使用多个类型参数:

typescript
function pair<T, U>(first: T, second: U): [T, U] {
    return [first, second];
}

const p1 = pair<string, number>("hello", 42);
const p2 = pair(123, "world");  // 类型推断

实际应用场景

1. 通用数据容器

typescript
class Container<T> {
    private data: T;

    constructor(value: T) {
        this.data = value;
    }

    getData(): T {
        return this.data;
    }

    setData(value: T): void {
        this.data = value;
    }
}

const numberContainer = new Container<number>(123);
const stringContainer = new Container<string>("hello");

2. API 响应处理

typescript
interface ApiResponse<T> {
    data: T;
    status: number;
    message: string;
}

async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
    const response = await fetch(url);
    return response.json();
}

// 使用示例
interface User {
    id: number;
    name: string;
}

const result = await fetchData<User>('/api/user/1');
console.log(result.data.name);

总结

  1. 命名约定:通常使用单个大写字母作为类型参数名称

    • T 表示 Type
    • K 表示 Key
    • V 表示 Value
    • E 表示 Element
  2. 适度使用:不是所有地方都需要泛型,过度使用会增加代码复杂度

  3. 提供默认类型:可以为泛型参数提供默认类型

typescript
interface Config<T = string> {
    id: T;
    name: string;
}

const config: Config = { id: "123", name: "test" };  // T 默认为 string
const numConfig: Config<number> = { id: 123, name: "test" };

泛型是 TypeScript 中最强大的特性之一,合理使用可以大大提高代码的复用性和类型安全性。在实际开发中,我们经常在以下场景中使用泛型:

  • 编写通用工具函数
  • 实现数据结构(如栈、队列、树等)
  • 处理 API 请求响应
  • 状态管理(如 Redux)
  • 依赖注入系统