跳到主要内容

TypeScript 泛型详解

在编程中,我们常常需要写一些可重用的函数或类。但是由于传入的数据类型不确定,导致函数或类的参数和返回值类型难以定义。这时,泛型就派上用场了。

泛型基础

泛型函数

泛型函数允许我们定义一个函数,在调用时再指定参数和返回值的具体类型。看一个简单的例子:

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

在上面的函数中,我们使用<T>定义了一个类型变量T,它代表着任意的类型。调用 identity 函数时,编译器会根据传入参数的类型来推断T的具体类型:

let output1 = identity<string>('myString'); // type of output1 is string
let output2 = identity<number>(100); // type of output2 is number

泛型接口

除了函数,接口也可以使用泛型。看下面的例子:

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

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

let myIdentity: GenericIdentityFn<number> = identity;

这里我们定义了一个泛型接口GenericIdentityFn,它描述了一类接收一个类型参数T并返回相同类型T的函数。

泛型类

与泛型接口类似,类也可以使用泛型。例如:

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;
};

GenericNumber类接受一个类型参数T,用来指定zeroValue属性和add方法的类型。创建实例时需要传入T的具体类型。

泛型约束

extends

有时我们希望限制泛型的范围,只允许传入某些类型。这时可以使用extends关键字:

interface Lengthwise {
length: number;
}

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

现在loggingIdentity函数只接受带有length属性的类型,如stringArrayNodeList等。如果传入其他类型则会报错。

keyof

keyof操作符可以用于获取某种类型的所有键,其返回类型是联合类型。例如:

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

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number

keyof经常与extends一起使用,用于限制泛型类型必须是另一个类型的键:

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

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

getProperty(x, 'a');
getProperty(x, 'm'); // error: Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

上面的getProperty函数要求第二个参数key必须是第一个参数obj的键。