一、什么是泛型
在开发过程中,我们不仅要创建定义一个好的类,接口,方法等,同时要考虑重用性,不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
可以提供泛型约束的成员:
- 类的实例成员
- 类的方法
- 函数参数
- 函数返回值
eg:
平时我们定义与一个函数
function getValue(value:Number) :Number{
return value
}
这里我们将Number类型分配给参数和返回类型,使该函数仅可用于该原始类型。但该函数并不是可扩展或通用的,很明显这并不是我们所希望的。
那么引入泛型以后,我们可以解决只能返回特定类型的问题
function getValue<T>(value:T) :T{
return value
}
getValue<string>('hello') //'hello'
getValue<Number>(123) //123
注意:除了为类型变量显式设定值之外,一种更常见的做法是使编译器自动选择这些类型,从而使代码更简洁。我们可以完全省略尖括号
二、使用方法与场景
泛型接口
interface Identities<V, M> {
value: V,
message: M
}
在上述的 Identities 接口中,我们引入了类型变量 V 和 M,来进一步说明有效的字母都可以用于表示类型变量,之后我们就可以将 Identities 接口作为 identity 函数的返回类型:
function identity<T, U> (value: T, message: U): Identities<T, U> {
console.log(value + ": " + typeof (value));
console.log(message + ": " + typeof (message));
let identities: Identities<T, U> = {
value,
message
};
return identities;
}
console.log(identity(68, "Semlinker"));
泛型类
在类中使用泛型也很简单,我们只需要在类名后面,使用 <T, ...> 的语法定义任意多个类型变量
class myClass<T>{
value:T
constructor(value:T){
this.value=value
}
getValue():T{
return this.value
}
}
const myNumber=new myClass<Number>(1)
myNumber.getValue() //1
const myString=new myClass<string>('hello')
myString.getValue() //hello
泛型约束与安全
有时我们可能希望限制每个类型变量接受的类型数量,这就是泛型约束的作用。可以确保属性是否存在,检查对象上的键是否存在。
确保属性存在
有时候,我们希望类型变量对应的类型上存在某些属性。这时,除非我们显式地将特定属性定义为类型变量,否则编译器不会知道它们的存在。
function identity<T>(arg: T): T {
console.log(arg.length); // Error
return arg;
}
在这种情况下,编译器将不会知道
T确实含有length属性,所以会报错
我们可以使用extends一个接口
interface Length {
length: number;
}
function identity<T extends Length>(arg: T): T {
console.log(arg.length); // 可以获取length属性
return arg;
}
T extends Length 用于告诉编译器,我们支持已经实现 Length 接口的任何类型。之后,当我们使用不含有 length 属性的对象作为参数调用 identity 函数时,TypeScript 会提示相关的错误信息
检查对象上的键是否存在
泛型约束的另一个常见的使用场景就是检查对象上的键是否存在.
keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
interface Person {
name: string;
age: number;
location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number
通过 keyof 操作符,我们就可以获取指定类型的所有键,之后我们就可以结合前面介绍的 extends 约束,即限制输入的属性名包含在 keyof 返回的联合类型中。具体的使用方式如下:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
总结
泛型是typescript比较常用的特性,其他语言中都存在泛型的概念,使用泛型解决了重复代码的问题,使用泛型约束,确保了代码的安全性和灵活性。