看到 TypeScript 泛型就头晕?给这是我开的方子!
阅读须知:本文示例的运行环境是 TypeScript 官网的 Playground,对应的编译器版本是 v3.8.3。
一、泛型简介
软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。
二、为什么要使用泛型
假设我们现在有一个 merge
工具方法,用于合并两个对象的属性,它的实现很简单:
function merge(objA, objB) {
return Object.assign(objA, objB);
}
创建完 merge 方法,我们可以这样使用:
const mergedObj = merge({ name: "Semlinker" }, { age: 30 });
// type MergeReturnType = (objA: any, objB: any) => any
type MergeReturnType = typeof merge;
// type MergedType = any
type MergedType = typeof mergedObj
通过观察以上信息,我们可以发现合并后 mergedObj
对象的类型是 any
。在 TypeScript 中,任何类型都可以被归为 any 类型。any
类型本质上是类型系统的一个逃逸舱。作为开发者,这给了我们很大的自由:TypeScript 允许我们对 any
类型的值执行任何操作,而无需事先执行任何形式的检查。当然自由太大也是不好的,这让我不禁想起,"情歌王子" 张信哲作品 <<过火>> 中的歌词:
怎么忍心怪你犯了错 是我给你自由过了火
让你更寂寞 才会陷入感情漩涡
怎么忍心让你受折磨 是我给你自由过了火
如果你想飞 伤痛我揹
TypeScript 开发团队,也怕给 any
类型的自由过了火,在 TypeScript 3.0 引入了新的 unknown
类型,它是 any
类型对应的安全类型。相比 any
类型,unknown
类型会更加严格,在对 unknown
类型的值执行大多数操作之前,我们必须进行某种形式的检查。
好的,下面让我们继续来分析 merge
方法存在的问题。很明显 merge 方法返回 any 类型,这并不是我们所希望的。那么如何解决这个问题呢?针对这个问题,我们可以使用泛型。现在我们来重构一下代码:
function merge<T, U>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
在上面代码中,我们引入了 T
和 U
两个类型变量,它是一种特殊的变量,只用于表示类型而不是值。类型变量可以自动帮助我们捕获用户传入的类型,之后我们就可以使用已捕获的类型。
对于
T
和U
类型变量不了解的小伙伴们,也不要着急,后面章节会有专门的讲解哟。
这时我们再来获取 merge
函数和 mergedObj
对象的类型:
const mergedObj = merge({ name: "Semlinker" }, { age: 30 });
// type MergeReturnType = <T, U>(objA: T, objB: U) => T & U
type MergeReturnType = typeof merge;
/*
* type MergedType = {
* name: string;
* } & {
* age: number;
* }
*/
type MergedType = typeof mergedObj;
观察以上结果,很明显此时 mergedObj
对象的类型已经不是 any
类型了,而是交叉类型。那么引入泛型后的 merge
方法就没有问题了么?非也,非也!请看以下代码:
const mergedObj = merge({ name: "Semlinker" }, 30);
console.log(mergedObj); // { name: "Semlinker" }
在上面代码中,我们使用了数值类型的数据作为参数调用了 merge
方法,而我们创建 merge
方法的最初目的是为了合并两个对象的属性,那么如何限制输入参数的类型呢?对于上述的 merge
方法来说,我们期望限制输入参数的类型是 object
类型。针对这个问题,我们就可以利用泛型约束,来解决这个问题。
三、extends 泛型约束
Preview the first 20% of the content for free.