1. 泛型函数
定义一个 identity 函数
Copy function identity<T>(arg: T): T {
return arg;
}
给 identity 函数添加了 类型变量T
. T 会捕获用户传入的类型,(比如: number),之后 T
就代表这个类型了。
这个版本的 identity函数 叫做泛型函数,因为 T 可以是任意的类型。
泛型函数的两种调用方法:
Copy 1. 传入所有的参数,包括类型参数,其中类型参数用 <> 括起来
const output = identity<string>('123');
即明确指定 T 就是 string, 因此会限制 arg 必须为 string 类型。
因此下面的代码会报错:
const output = identity<string>(123); 因此 123 不是 string 类型,所以会报错。
2. 可以不传类型参数,编译器自己查看 '123' 的类型,自动推算出 T 为 string
const output = identity('123');
2. 泛型变量
想象下以下代码,因为 return 的值必然是个字符串,而 T 不一定是 string, 所以会报错。
Copy function identity<T>(arg: T): T {
return arg+'123';
}
又或者:
Copy function identity<T>(arg: T): T {
console.log(arg.length); // T 类型不一定有 length 属性(T 为 string 时有),所以会报错
return arg;
}
又因为数组具有 .length
属性,因此可以像下面这样:
Copy function logIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // 数组中确定是有length属性的
return arg;
}
即我们可以把泛型变量T
作为类型的一部分,实际类型 T[]
由 T
构成,增加了灵活性。
3. 泛型类型
Copy function identity<T>(arg: T): T {
return arg;
}
const myIdentity: <T>(arg: T) => T = identity
这段代码其实就是 const myIdentity = identity;
<T>(arg: T) => T 只是类型描述
const myIdentity: <T>(arg: T) => T = identity
又会等同于
const myIdentity: { <T>(arg: T): T } = identity
因此我们可以定义一个泛型接口:
Copy interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T{
return arg;
}
const myIdentity: GenericIdentityFn = identity
接受参数的泛型接口:
Copy // 接受一个类型参数,作为T
// 这样接口里的其他成员也能知道这个参数的类型
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T) :T {
return arg;
}
const myIdentity: GenericIdentityFn<number> = identity;
4. 泛型类
与泛型接口类似
Copy class GenericNumber<T> {
zeroValue: T;
add: (x:T, y:T) => T;
}
const myGenericNumber = new GenericNumber<number>(); // 必须传入类型 number
myGenericNumber.zeroValue = 1;
myGenericNumber.add = function(x,y) = { return x + y };
类有静态部分和实例部分,泛型类只能用于实例部分的类型,类的静态属性不能使用这个泛型类型。
5. 泛型约束
首先以下代码会报错:
Copy function logIdentity<T>(arg: T):T {
console.log(arg.length); // 不一定会有 .length 属性
return arg;
}
因此我们需要约束类型 T
必须要有 length
属性, 我们可以定义一个接口来描述约束条件,使用接口和extends
关键字来实现约束:
Copy interface LengthWise {
length: number;
}
function identity<T extends LengthWise>(arg: T) :T {
console.log(arg.length); // 不会报错,接口限制了类型 T 会有 length 属性
return arg;
}
此时,要调用 identity
, 那么参数必须有 length
属性
5.1 泛型约束中使用类型参数
声明一个类型参数,被另一个类型参数所约束。
Copy function getProperty<T, K extends keyof T>(obj: T, key:K) {
return obj[key];
}
const x = {a: 1, b :2};
getProperty(x,'a')
getProperty(x,'m') // m 不是对象 x 的key,因此会报错
关于 keyof
:
Copy interface Person {
name: string;
age: number;
location: string;
}
type k1 = keyof Person; // "name" | "age" | "location" 对象的属性名
type k1 = keyof Person[]; // "length" | "push" | "pop" 数组的属性名
因此 K extends keyof T
,则 K
必须是对象 T
的某个属性名
5.2 泛型中使用类类型