泛型
# 前言
泛型
是为了构建通用化的组件所设计的。在强类型语言中,泛型
是构建大型复杂系统的主要特性之一,它允许将类型定义通用化,而不是单一的形式。
# 基本使用
function doSomeThing<T>(arg: T): T {
return arg;
}
2
3
相比于我们之前学习的类型定义的方式,这里我们多了一个<T>
,在TypeScript中,这就叫泛型
,表示的语义为,当你调用这个函数的时候除了传递常用的参数,你也可以传递类型,内部会使用传递的这个类型。
比如,我们传递一个string
类型:
doSomeThing<string>("mss")
值得注意的是,当你使用了泛型
,TypeScript会默认会通过外部传入的类型,推断内部变量或者函数的返回值的类型,并且强制内部的类型要符合外部传入的类型,否则,TypeScript会报错:
function doSomeThing<T>(arg: T): T {
console.log(arg.length)
return arg;
}
// Property 'length' does not exist on type 'T'
2
3
4
5
6
但是我们有的数据类型就是存在length
属性的,那我们这时应该怎么做呢?比如数组来说,TypeScript允许我们直接定义数组的泛型:
function deArr<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
2
3
4
此时我们访问length
就是合法的。
同时,定义数组的泛型也可以这样写,这种方式和上面那种方式是等效的:
function deAnotherArr<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg
}
2
3
4
这种方式也是可以,完全取决于你个人的喜好。
# 使用泛型类型
基本使用
function doSomeThing<T>(arg: T): T {
console.log(arg.length)
return arg;
}
let myDoSomeThing: <T>(arg: T) => T = doSomeThing;
2
3
4
5
6
传入的类型参数,也可以不用和函数定义的T
保持一致:
let myDoSomeThing: <U>(arg: U) => U = doSomeThing;
这样也是可以的。
# 泛型接口
基本使用:
我们首先定义一个泛型
接口:
interface GenericInterFace {
<T>(arg: T): T
}
2
3
然后我们这样使用:
let genericInterFace: GenericInterFace = function get<T>(arg: T):T {
return arg;
}
2
3
也可以在定义接口的时候将传入泛型的类型:
interface GenericInterFace<T> {
(arg: T): T
}
2
3
使用的方式和上面的例子一样:
let genericInterFace: GenericInterFace = function get<T>(arg: T):T {
return arg;
}
2
3
此外,前面的例子里面,当我们使用泛型的时候,由于TypeScript编译器不能推断传入的T
的类型,当然这也不可能推断,所以当我们试图在内部访问length
属性的时候,就会报错。然后,我们这里可以利用接口,在编译之前就给开发一个友好的提示:
定义一个length
的接口:
interface Lengthwise {
length: number;
}
2
3
然后重写我们前面的doSomeThing
例子:
function doSomeThing<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg;
}
2
3
4
此时就不会报错,这里已经确保了传入的参数必定是有length
属性的。
# 泛型类
泛型类在结构上和泛型接口差不多。
class GenericClass<T> {
name: T
add: (x: T, y:T) => T
}
2
3
4
然后我们可以正常的通过new
关键字进行实例化。
let genericClass = new GenericClass();
genericClass.name = 'mss';
2
当然,使用泛型的一个好处就是你可以传入任意类型的类型参数:
let genericClass = new GenericClass<string>();
这也是合理的。
# 泛型的高级用法
1、使用泛型确保访问对象时不会访问到对象不存在的属性。
定义一个泛型:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
2
3
注意这里是的keyof
是TypeScript中给我们提供的遍历对象属性值的操作符。
然后我们这样使用:
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
// 1
getProperty(x, "m");
// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.
2
3
4
5
6
7
2、类的构造函数和泛型一起使用
function create<T>(c: { new (): T }): T {
return new c();
}
2
3