TypeScript 类、泛型的使用实践记录 | 豆包MarsCode AI刷题

53 阅读3分钟

TypeScript 类与泛型的使用实践记录

在 TypeScript 中,泛型是提升代码灵活性和可重用性的重要工具,特别适用于开发通用类、函数或数据结构。本文将探讨 TypeScript 泛型的基础用法、应用场景,以及结合类型约束提升代码安全性和表达力的技巧。

一、泛型的基本概念

泛型(Generics) 是一种在定义函数、接口或类时,不预先指定具体的类型,而是在使用时再指定的特性。泛型的核心思想是“类型参数化”,它能让代码更加灵活且类型安全。

二、泛型的使用方法

1. 泛型函数

泛型函数允许我们在定义时不指定参数的具体类型,而是在调用时动态传入类型参数。

示例

function identity<T>(arg: T): T {
    return arg;
}

// 调用时指定类型
const output1 = identity<string>("Hello, TypeScript");

// 类型推断
const output2 = identity(42); // 推断 T 为 number

应用场景

• 处理返回值类型与参数类型一致的情况,如包装函数、数据处理等。

2. 泛型接口

泛型可以应用于接口,用于定义更加灵活的结构。

示例

interface KeyValuePair<K, V> {
    key: K;
    value: V;
}

const pair: KeyValuePair<string, number> = { key: "age", value: 30 };

应用场景

• 定义通用的数据结构,如键值对、树节点等。

3. 泛型类

泛型类用于定义支持多种数据类型的通用类。

示例

class Stack<T> {
    private items: T[] = [];
    
    push(item: T): void {
        this.items.push(item);
    }

    pop(): T | undefined {
        return this.items.pop();
    }

    peek(): T | undefined {
        return this.items[this.items.length - 1];
    }
}

const stringStack = new Stack<string>();
stringStack.push("TypeScript");
console.log(stringStack.pop()); // 输出 "TypeScript"

const numberStack = new Stack<number>();
numberStack.push(42);
console.log(numberStack.peek()); // 输出 42

应用场景

• 创建通用容器类,如栈、队列、链表等。

4. 泛型约束

泛型默认可以接受任意类型,但有些场景需要对类型参数施加约束,以限制其范围。

使用 extends 定义约束

interface HasLength {
    length: number;
}

function logLength<T extends HasLength>(arg: T): void {
    console.log(arg.length);
}

logLength("Hello");  // 字符串有 length 属性
logLength([1, 2, 3]); // 数组有 length 属性
// logLength(42);    // 错误:number 没有 length 属性

应用场景

• 需要使用特定属性或方法的类型时,如数组操作、对象操作等。

三、实际开发中的应用场景

1. 数据处理中的泛型

在处理复杂数据结构时,泛型可以确保数据类型一致性。

示例:通用的过滤函数

function filterArray<T>(arr: T[], predicate: (value: T) => boolean): T[] {
    return arr.filter(predicate);
}

const numbers = [1, 2, 3, 4];
const evenNumbers = filterArray(numbers, (n) => n % 2 === 0);
console.log(evenNumbers); // [2, 4]

2. API 响应处理

在处理 RESTful API 响应时,泛型能动态指定返回数据类型。

示例

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

function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
    return fetch(url)
        .then(response => response.json());
}

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

fetchApi<User[]>('/api/users').then(response => {
    console.log(response.data); // 推断为 User[]
});

3. React 中的泛型

在 React 中,泛型常用于定义组件的 props。

示例

interface ListProps<T> {
    items: T[];
    renderItem: (item: T) => JSX.Element;
}

function List<T>({ items, renderItem }: ListProps<T>) {
    return (
        <ul>
            {items.map((item, index) => (
                <li key={index}>{renderItem(item)}</li>
            ))}
        </ul>
    );
}

// 使用 List 组件
interface User {
    id: number;
    name: string;
}

const users: User[] = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" }
];

<List 
    items={users}
    renderItem={(user) => <span>{user.name}</span>}
/>;

四、类型约束提升代码安全性

结合联合类型和条件类型的约束

泛型可以结合条件类型和联合类型,进一步增强灵活性。

示例:实现一个键值提取工具

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

const user = { id: 1, name: "Alice" };
console.log(getProperty(user, "name")); // 输出 "Alice"
// console.log(getProperty(user, "age")); // 错误:'age' 不存在于类型 'user'

优势

• 避免访问对象中不存在的属性。

• 提升开发过程中的类型安全性。

五、总结

TypeScript 泛型的核心在于提供灵活的类型参数化能力,适合于各种需要通用性和类型安全的场景。通过结合泛型函数、类、接口以及类型约束,可以实现高效且安全的代码。在实际开发中,合理应用泛型不仅能提升代码复用性,还能显著减少类型错误,提高开发效率和代码质量。