TypeScript 泛型


泛型简介

泛型在 TS 中可以说是一个非常重要的属性,它承载了从静态定义到动态调用的桥梁,同时也是 TS 对自己类型定义的元编程。泛型可以说是 TS 类型工具的精髓所在,也是整个 TS 最难学习的部分。

可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。

设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。


泛型定义

下面来创建第一个使用泛型的例子:identity函数。 这个函数会返回任何传入它的值,不用泛型的话,这个函数可能是下面这样:

function identity(value: any): any {
    return value;
}

使用 any 类型会导致这个函数可以接收任何类型的参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。

因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。

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

我们给 identity 添加了类型变量 TT 帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T 当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。

在这里插入图片描述

我们把这个版本的 identity 函数叫做泛型,因为它可以适用于多个类型。 不同于使用 any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。


泛型使用

我们定义了泛型函数后,可以用两种方法使用。

1. 传入所有的参数,包含类型参数

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

let output = identity<string>("myString"); 

这里调用函数我们明确的指定了 T 是 string类型(identity<string>),
这时 函数入参(value) 和 函数返回值 也必须都是 string类型,否则就会报错。

2. 类型推论

编译器会根据传入的参数自动地帮助我们确定 T 的类型 (普遍使用)

在这里插入图片描述

这里调用函数我们没有使用尖括号(<>)来明确地传入类型,
这时编译器可以自动推论出 myString 的类型,然后把 T 设置为它的类型。

类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入 T 的类型,在一些复杂的情况下,这是可能出现的。

3. 定义多个类型变量

其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。
比如我们引入一个新的类型变量 U,用于扩展我们定义的 identity 函数:

function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity<Number, string>(68, "Semlinker"));

在这里插入图片描述
利用上面 类型推论 可简写为:

identity<Number, string>(68, "Semlinker");
// 可省略尖括号,简写为:
identity(68, "Semlinker");

4. 使用泛型变量

如果我们想同时打印出 value 的长度。 我们很可能会这样做:

function identity<T>(value: T): T {
    console.log(value.length);  // 报错:类型 “T” 上不存在属性 “length”
    return value;
}

如果这么做,编译器会报错说 类型 “T” 上不存在属性 “length” ,但是没有地方指明 value 具有这个属性。
记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有 .length属性的。

此时,我们可以将 T 进一步设置成 T 类型的数组 即可:

function identity<T>(value: T[]): T[] {
    console.log(value.length);
    return value;
}

泛型函数identity,接收类型参数 T参数 value,此时参数和返回值是 T 的数组
这可以让我们把泛型变量 T 当做类型的一部分使用,而不是整个类型,增加了灵活性。


泛型接口

interface Identities<V, M> {
    value: V;
    message: M;
}

function identity<T, U>(value: T, message: U): Identities<T, U> {
    let identities = {
        value,
        message,
    };
    return identities;
}

identity(68, 'Semlinker'); // {value: 68, message: "Semlinker"}

在上述的 Identities 接口中,我们引入了类型变量 VM,来进一步说明有效的字母都可以用于表示类型变量,之后我们就可以将 Identities 接口作为 identity 函数的返回类型

posted @ 2022-07-20 18:15  猫老板的豆  阅读(15)  评论(0编辑  收藏  举报