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

93 阅读3分钟

TypeScript 类与泛型实践

一、泛型的基本概念

泛型是一种可以在定义函数、类或接口时使用的类型参数化工具。它允许我们编写可复用的代码,这些代码能够处理不同类型的数据,而不是局限于特定的类型。

二、泛型函数

  1. 简单的泛型函数示例
function identity<T>(arg: T): T {
    return arg;
}

let output1 = identity<string>("Hello"); // 明确指定类型参数为 string
console.log(output1); 

let output2 = identity(123); // 类型推断,T 被推断为 number
console.log(output2); 

在这个 identity 函数中,T 是一个泛型类型参数。函数接受一个类型为 T 的参数并返回相同类型的值。 2. 泛型函数的多个类型参数

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

let result = pair<string, number>("Hello", 42);
console.log(result); 

这里的 pair 函数有两个泛型类型参数 T 和 U,分别用于函数的两个参数,函数返回一个包含这两个不同类型值的元组。

三、泛型类

  1. 泛型类示例
class Box<T> {
    private content: T;

    constructor(item: T) {
        this.content = item;
    }

    public getContent(): T {
        return this.content;
    }
}

let box1 = new Box<string>("Some text");
console.log(box1.getContent()); 

let box2 = new Box<number>(42);
console.log(box2.getContent()); 

Box 类是一个泛型类,T 作为类型参数。类中的 content 属性和 getContent 方法都使用了这个泛型类型,使得 Box 类可以存储和获取不同类型的数据。

四、泛型的使用场景

(一)数据结构操作

  1. 数组操作函数
function arrayMap<T, U>(arr: T[], callback: (item: T) => U): U[] {
    return arr.map(callback);
}

let numbers = [1, 2, 3, 4];
let doubled = arrayMap<number, number>(numbers, (num) => num * 2);
console.log(doubled); 

let names = ["Alice", "Bob", "Charlie"];
let nameLengths = arrayMap<string, number>(names, (name) => name.length);
console.log(nameLengths); 

这个 arrayMap 函数可以对不同类型的数组进行映射操作,通过泛型类型参数 T 和 U 来适应不同类型的数组元素和映射结果类型。

(二)数据存储与获取

  1. 泛型缓存类
class Cache<T> {
    private cache: { [key: string]: T } = {};

    setItem(key: string, item: T): void {
        this.cache[key] = item;
    }

    getItem(key: string): T | undefined {
        return this.cache[key];
    }
}

let stringCache = new Cache<string>();
stringCache.setItem("message", "Hello, Cache");
console.log(stringCache.getItem("message")); 

let numberCache = new Cache<number>();
numberCache.setItem("count", 42);
console.log(numberCache.getItem("count")); 

Cache 类可以用于缓存不同类型的数据,通过泛型 T 来确定缓存值的类型。

五、类型约束

(一)使用 extends 关键字进行类型约束

  1. 约束函数参数类型
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

let str = "Hello";
loggingIdentity(str); 

let arr = [1, 2, 3];
loggingIdentity(arr); 

// 错误示例:对象没有 length 属性
// let obj = { name: "John" };
// loggingIdentity(obj); 

这里的 loggingIdentity 函数使用了类型约束,要求泛型类型 T 必须满足 Lengthwise 接口,即具有 length 属性。这样可以确保函数内部能够安全地访问 length 属性,增加了代码的安全性。

(二)在泛型类中使用类型约束

  1. 限制泛型类的类型参数
class SortedArray<T extends number | string> {
    private array: T[] = [];

    add(item: T): void {
        // 假设这里有排序逻辑
        this.array.push(item);
    }

    getArray(): T[] {
        return this.array;
    }
}

let numberArray = new SortedArray<number>();
numberArray.add(5);
numberArray.add(3);
console.log(numberArray.getArray()); 

let stringArray = new SortedArray<string>();
stringArray.add("Apple");
stringArray.add("Banana");
console.log(stringArray.getArray()); 

// 错误示例:不能使用布尔类型
// let boolArray = new SortedArray<boolean>();
// boolArray.add(true); 

SortedArray 类限制了泛型类型参数 T 只能是 number 或 string 类型,这样在类的方法中操作 T 类型的数据时,可以更有针对性地编写代码,提高代码的灵活性和安全性。 通过合理地使用泛型和类型约束,我们可以在 TypeScript 中编写更加通用、灵活且安全的代码,减少代码的重复编写,提高代码的可维护性和可扩展性。