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
属性的类型,如string
、Array
、NodeList
等。如果传入其他类型则会报错。
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
的键。