面向对象特性中, 最根本的就是面向对象的三大基本特征: 封装, 继承, 多态. 同时, TypeScript 中也存在多态的使用, 比如函数重载, 今天我们先看一下函数重载以及泛型的概念.
什么是函数重载
简单来说, 函数重载具有两个特征: 名称相同, 参数不同 (参数类型, 个数不同.) 所以, 函数重载的解释应该是具备不同参数的同名函数. 注意: 函数重载是多态的一种体现.
函数重载的声明和实现
TypeScript 中, 函数重载主要包括两部分: 函数声明, 和函数实现. 函数声明主要是 TSC 解析的一种声明体现, 实际编译中, 并不会编译成具体代码. 我们可以通过 TypeScript 的 playground https://www.tslang.cn/play/index.html 来查看.
1, 参数不同的函数重载
加入我们有一个打印函数, 可以打印输入的一个 string 信息, 我们可以将函数声明如下:
- // 函数声明
- function print(info: string): void;
而还有另一种情况, 就是输入的有可能是两个 string 类型的参数, 我们都需要打印下来, 于是我们的函数声明可以是这样:
- // 函数声明
- function print(info: string, message: string): void;
而当这两种声明, 同时存在 TypeScript 的声明文件中, 我们就需要用函数重载来实现, 这是 JavaScript 没有的特性.
而实现函数重载的要求就是, 我们要在一个更为宽泛的范围去实现函数重载, 所以, TypeScript 中的我们实现 print 函数如下:
- // 在更宽泛的范围, 我们用可选参数来实现重载
- function print(info: string, message ?: string) {
- let printValue: string = info;
- if(message){
- printValue += message;
- }
- console.log(printValue);
- }
2, 参数个数相同, 但类型不同的函数重载
函数重载的第二种情况, 参数个数相同, 但是参数类型不一样, 这种情况下也可以通过重载来实现.
比如, 上述打印信息的函数, 有可能接受的输入是一个 string 字符串, 也有可能输入接受的是一个 number 类型的数字, 那么我们第一步的函数声明便是如下:
- function print(info: string): void;
- function print(num: number): void;
从上可以看到, 我们的函数声明中, 参数的类型是不同的, 在这种情况下, TypeScript 是如何在一个宽泛的范围内实现呢? 这里就要用到联合类型, 如下所示:
- function print(message: string | number) {
- console.log(message)
- }
函数重载的总结
从我们实现两个函数重载的例子可以看出, 我们在 TypeScript 中实现函数重载的方式分别是利用了 TypeScript 中的两个类型特性: 可选类型以及联合类型.
所以, 如果从便捷的角度来讲, 我们如果是遇到了类似的实现, 其实可以直接使用可选参数和联合类型来实现自己想要的函数效果.
泛型
在函数重载的不同参数类型, 相同参数个数的重载中, 我们介绍了它的重载实现方式, 利用联合类型来实现, 但是如果要打印出来的类型有很多, 那么我们最终只能用 any 类型来实现 print 函数了.
但是, 如果用 any 类型实现一个可以打印任意值的 print 函数, 这样又让我们的函数变得类型缺失, 这个时候, 泛型这种解决方案也就应运而生.
什么是泛型
泛型指的是一种情况: 定义是可以是任意类型, 但是在编译的时候, 必须有明确的类型.
有点绕, 那么我们用泛型来实现上述第二个函数重载的例子, 结合这个例子, 可以体会一下这句话的含义.
- function print<T>(message: T) {
- console.log(message);
- }
在这个函数中, 泛型表示的方式是: 函数名称<泛型参数>(arg: 泛型参数).
这个函数在声明之后, 函数类型是一个泛型. 我们可以传递任意的类型参数到 print 函数中, 但是当我们传递一个 string 类型的时候, 这个函数便是一个 string 类型的函数了, 已经在 tsc 编译阶段开始明确指定类型, 这是和 any 函数所不一样的地方.
泛型的好处
首先, 我们不用定义过多的联合类型来让函数变得复杂而又冗长, 如:
- function print(arg: string | number | boolean | array | 自定义类型) {
- // 我们应该尽量避免多类型的传值函数, 此时我们应该用泛型来实现.
- }
其次, 泛型可以是任何类型, 但是在编译时一定是类型确定的. 而且泛型也可以有继承属性, 可以继承接口获取更多的类型定义等.
- function print<T extends Interface> (arg: T) {
- // 通过继承, 来让泛型有更多的可变性.
- }
最后, 类型别名也可以是泛型, 如我们可以做如下类型定义:
type Person<T> = { age: T }
泛型总结
总体来说, 利用泛型, 也是为了更准确的让我们使用类型思维, 是为了更准确的描述参数, 或者声明的类型准确性, 如果能够熟练的掌握泛型, 那么在 TypeScript 的开发中, 将会有不一样的体验.
而常常以类型思维去思考 JavaScript 中的函数或者变量, 我们也就会减少很多因为类型方面的犯错, 使得我们的项目不仅更好的测试, 也会更少的出错.
不得不说, 在大前端领域, 类型思维的缺失的确是个普遍现象, 如果将类型思维捡起来, 将会是一个可能存在着痛苦的过程, 但是我相信, 如果你做到了, 那么你不仅会在开发代码的时候会更谨慎, 能开发出更优秀的应用程序, 还会体验到前端行业别样的魅力.
我的博客地址: http://www.gaoyunjiao.fun/?p=130
来源: https://www.cnblogs.com/qixingduanyan/p/11470851.html