Typescript - 7. 泛型(Generics)

110 阅读2分钟

泛型(Generics)是 TypeScript 中的一种强大工具,用于构建可重用的组件。通过使用泛型,可以创建可以处理任意类型的函数、类和接口,而不必在定义时锁定具体类型。

泛型函数

定义泛型函数时,可以在函数名后面的尖括号 <T> 中指定一个或多个类型变量。

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

let output1 = identity<string>("Hello World");
let output2 = identity<number>(100);

console.log(output1); // "Hello World"
console.log(output2); // 100

TypeScript 可以根据传入的参数自动推断类型,因此可以省略类型参数:

let output3 = identity("Hello TypeScript"); // 类型推断为 string

泛型接口

可以使用泛型定义接口,以便接口中的属性和方法都可以使用这个泛型类型。

interface GenericIdentityFn<T> {
    (arg: T): T;
}

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

let myIdentity: GenericIdentityFn<number> = identity;
console.log(myIdentity(123)); // 123

泛型类

类也可以使用泛型来创建灵活的和可重用的组件。

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;

    constructor(zeroValue: T, addFunction: (x: T, y: T) => T) {
        this.zeroValue = zeroValue;
        this.add = addFunction;
    }
}

let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);
console.log(myGenericNumber.add(5, 10)); // 15

泛型约束(Constraints)

有时需要对泛型类型进行约束,使其符合某些条件。可以使用 extends 关键字来实现。

interface Lengthwise {
    length: number;
}

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

loggingIdentity({ length: 10, value: 3 }); // 10

在泛型中使用类型参数

可以在一个泛型类型中使用另一个类型参数。

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

let x = { a: 1, b: 2, c: 3, d: 4 };
console.log(getProperty(x, "a")); // 1
// console.log(getProperty(x, "m")); // 错误:类型“'m'”不满足约束“'a' | 'b' | 'c' | 'd'”。

泛型默认值

可以为泛型类型参数指定默认类型。

function createArray<T = string>(length: number, value: T): T[] {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

let stringArray = createArray(3, "hello"); // 默认类型 string
console.log(stringArray); // ["hello", "hello", "hello"]

let numberArray = createArray<number>(3, 42); // 指定类型 number
console.log(numberArray); // [42, 42, 42]

泛型工具类型

TypeScript 提供了一些内置的工具类型,例如 PartialReadonlyRecord 等,来帮助处理泛型。

interface Person {
    name: string;
    age: number;
}

// Partial 将所有属性变为可选
type PartialPerson = Partial<Person>;

// Readonly 将所有属性变为只读
type ReadonlyPerson = Readonly<Person>;

// Record 创建一个具有指定键和类型的对象类型
type StringDictionary = Record<string, number>;

泛型提供了一种灵活且类型安全的方式来构建可重用的代码组件,使得代码更加通用和易维护。在 TypeScript 中,泛型广泛用于函数、类、接口和工具类型,极大地增强了 TypeScript 的表达能力。