Typescript 泛型

在比如 C# 和 Java 语言中,用来创建可复用组件的工具,我们称之为泛型(generics)。利用泛型,可以创建支持多类型的组件,从而减少不必要的工作。

使用泛型类型变量

编译器会强制你在函数体内,正确的使用这些类型参数。假如我们在泛型参数中传入了number,则无法在函数体内调用该参数的length属性。

1
2
3
4
5
function loggingIdentity<Type>(arg: Type): Type {
console.log(arg.length);
// Property 'length' does not exist on type 'Type'.
return arg;
}

可以通过更改实参arg的参数类型来规避该情况:

1
2
3
4
function loggingIdentity<Type>(arg: Type[]): Type[] {
console.log(arg.length);
return arg;
}

泛型类型

改写的函数

1
2
3
function identity<Type>(arg: Type): Type {
return arg;
}

泛型函数

1
let myIdentity: <Type>(arg: Type) => Type = identity;

泛型类型参数(Type)可以使用不同名字,只要数量和使用方式一致即可

也可以以对象类型的调用签名的形式,书写这个泛型类型

1
let myIdentity: { <Type>(arg: Type): Type } = identity;

泛型接口

1
2
3
4
5
6
7
8
9
interface GenericIdentityFn {
<Type>(arg: Type): Type;
}

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

let myIdentity: GenericIdentityFn = identity;

有的时候,我们会希望将泛型参数作为整个接口的参数,这可以让我们清楚的知道传入的是什么参数

1
2
3
4
5
6
7
8
9
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}

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

let myIdentity: GenericIdentityFn<number> = identity;

当要描述一个包含泛型的类型时,理解什么时候把类型参数放在调用签名里,什么时候把它放在接口里是很有用的。

个人理解,当参数类型或返回值类型确定时,可以使用第二种去约束。类似于一种把类型校验提高了一级这种感觉。

泛型类

写法上类似于泛型接口

1
2
3
4
5
6
7
8
9
10
class GenericNumber<NumType> {
zeroValue: NumType;
add: (x: NumType, y: NumType) => NumType;
}

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

一个类它的类型有两部分:静态部分和实例部分。泛型类仅仅对实例部分生效,所以当我们使用类的时候,注意静态成员并不能使用类型参数

类中的constructor就是静态类型。可以通过一个create函数先去实现接口从而规范静态类型,通过这个create函数去new class进而实现实例部分的规范。

泛型约束

通过让泛型参数继承接口达到约束的效果

1
2
3
4
5
6
7
8
interface Lengthwise {
length: number;
}

function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}

在泛型约束中使用类型参数

可以声明一个类型参数,这个类型参数被其他类型参数约束

举个例子,我们希望获取一个对象给定属性名的值,为此,我们需要确保我们不会获取 obj 上不存在的属性。所以我们在两个类型之间建立一个约束

1
2
3
4
5
6
7
8
9
10
11
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a");
getProperty(x, "m");

// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

在泛型中使用类类型

1
2
3
function create<Type>(c: { new (): Type }): Type {
return new c();
}

和之前说的规避class的问题类似,创建了一个create函数

作者

徐云飞

发布于

2022-10-26

更新于

2023-02-05

许可协议

评论