TypeScript中的泛型的使用方法和场景

89 阅读4分钟

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

探讨TypeScript中的泛型的使用方法和场景

1. 泛型的基本概念

泛型(Generics)是指在定义函数、接口或类时,不预先指定具体的类型,而是在使用时再指定类型的一种特性。通过使用泛型可以增加代码的可复用性,提高代码的灵活性。

2. 泛型的使用场景

  • 数据结构的抽象化:通过使用泛型,我们可以编写出通用的数据结构,例如堆栈、队列或链表等。这样一来,我们可以将这些数据结构应用于不同的数据类型,并且保持代码的可重用性和类型安全性。
  • 函数的通用化:使用泛型函数可以处理多种输入类型,从而提高代码的通用性和复用性。例如,可以编写一个泛型排序函数,使其适用于不同类型的数组排序。
  • 接口和类的通用化:在定义接口和类时,使用泛型可以使其更加通用和灵活。可以在实例化时指定具体的类型,从而确保该接口或类仅适用于特定类型的对象。
  • 第三方库的类型安全:当使用第三方JavaScript库时,可以通过为其编写类型声明文件来提供类型安全性。通过使用泛型,可以为这些库添加更详细和准确的类型信息。

3、泛型的使用方法

泛型函数

泛型函数可以接收任意类型的参数,并返回相同类型的结果。

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

let output = identity<string>("myString");  // 类型为 "string"
let output2 = identity<number>(100);        // 类型为 "number"
泛型接口

泛型接口可以定义一个类型变量,然后在接口的多个属性中使用这个变量。

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

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

let myIdentity: GenericIdentityFn<number> = identityFn;
泛型类

泛型类允许你在类的层面上定义类型变量。

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

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型约束

你可以为泛型添加约束,以确保它们符合特定的形状或接口。

typescript
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 });  // OK
泛型类型别名

你可以创建泛型类型别名来为复杂类型创建别名。

typescript
type Container<T> = {
    value: T;
};

const myContainer: Container<number> = { value: 1 };
泛型联合类型

你可以创建泛型联合类型,以允许函数接受多种类型的参数。

typescript
function padLeft<T extends string | number>(input: T, padding: string | number): string {
    if (typeof input === "number") input = input.toString();
    if (typeof padding === "number") padding = padding.toString();

    return padding + input;
}

padLeft("Ex", 5);  // 返回 "5Ex"
padLeft(2, "Ex");  // 返回 "Ex2"
泛型工具类型

TypeScript 提供了一些内置的泛型工具类型,如 Partial<T>, Readonly<T>, Pick<T, K> 等,用于创建类型的子集或选择类型的某些属性。

typescript
type PartialPoint = Partial<{ x: number; y: number }>;
const point: PartialPoint = { x: 1 };  // OK
泛型条件类型

条件类型允许你基于类型系统的条件逻辑来创建新的类型。

typescript复制
type IsNumber<T> = T extends number ? "Yes" : "No";
type A = IsNumber<5>;  // "Yes"
type B = IsNumber<"5">;  // "No"

如何使用类型约束来增加代码的灵活性和安全性

1. 泛型约束的基本使用

在使用泛型时,我们经常需要限制泛型类型必须满足某些条件。这就是泛型约束的作用。通过extends关键字,我们可以指定泛型类型必须包含某些属性或方法。这种约束确保了我们可以安全地访问这些必需的成员。

2. 泛型约束的多重约束

在某些场景下,我们可能需要类型同时满足多个接口的要求。TypeScript允许我们使用交叉类型来实现多重约束,这为类型系统提供了更强的表达能力。

3. 泛型约束的实际应用

  • 确保类型具有特定属性:例如,确保传入的类型具有length属性时,可以这样做:
    interface Lengthwise {
        length: number;
    }
    function loggingIdentity<T extends Lengthwise>(arg: T): T {
        console.log(arg.length);  // 现在我们知道arg一定有length属性
        return arg;
    }
    
    这样,我们可以确保arg一定有length属性,增强了类型安全性。

通过合理使用泛型和类型约束,我们可以编写出更加健壮、可维护和易于理解的TypeScript代码。