TypeScript中的方法重载详解


Posted in Javascript onApril 12, 2019

前言

方法重载(overload)在传统的静态类型语言中是很常见的。JavaScript 作为动态语言, 是没有重载这一说的。一是它的参数没有类型的区分,二是对参数个数也没有检查。虽然语言层面无法自动进行重载,但借助其动态的特性,我们可以在代码中手动检查入参的类型,或者通过 arguments 获取到参数个数,从而实现根据不同的入参做不同的操作。

比如有一个获取聊天消息的方法,根据传入的参数从数组中查找数据。如果入参为数字,则认为是 id,然后从数据源中找对应 id 的数据并返回,否则当成类型,返回这一类型的消息。

function getMessage(query) {
 if (typeof query === "nunber") {
 return data.find(message => message.id === query);
 } else {
 return data.filter(message => message.type === query);
 }
}

TypeScript 中,假如我们的消息数据为如下结构:

type MessageType = "string" | "image" | "audio";

type Message = {
 id: number;
 type: MessageType;
 content: string;
};

上面获取数据的方法等价于:

function getMessage(
 query: number | MessageType
): Message[] | Message | undefined {
 if (typeof query === "number") {
 return data.find(message => message.id === query);
 } else {
 return data.filter(message => message.type === query);
 }
}

这样做一是类型书写上比较丑陋,二是没有发挥出 TypeScript 类型检查的优势,这里我们是可以根据入参的类型明确知道返回的类型的,即如果传入的是 id,返回的是单个数据或undefined,如果是根据类型查找,返回的是数组。而现在调用方法后,得到的类型太过宽泛,这和使用 any 做为返回没多大差别。

TypeScript中的方法重载详解

函数返回类型不够紧凑

因为类型的不明朗,返回的结果都不能直接操作,需要进行类型转换后才能继续。

const result1 = getMessage("audio");
/** 不能直接对 result1 调用数组方法 */
console.log((result1 as Message[]).length);

const result2 = getMessage(1);
if (result2) {
 /** 不能对 result2 直接访问消息对象中的属性 */
 console.log((result2 as Message).content);
}

重载的实现

这时候可通过提供多个函数类型的声明来解决上面的问题,最后得到的结果就是间接实现了函数的重载。当然这个重载只是 TypeScript 编译时的。

function getMessage(id: number): Message | undefined;
function getMessage(type: MessageType): Message[];
function getMessage(query: any): any {
 if (typeof query === "number") {
  return data.find(message => message.id === query);
 } else {
  return data.filter(message => message.type === query);
 }
}

这样改造后,我们在调用的时候直接就会有重载的提示。

TypeScript中的方法重载详解

实现 TypeScript 的重载后调用时的自动提示

并且得到的结果类型是重载方法中指定的入参与返回的组合,在对结果进行使用时,无须再进行类型转换。

const result1 = getMessage("audio");
/** ✅ 无须类型转换 */
console.log(result1.length);

const result2 = getMessage(1);
if (result2) {
 /** ✅ 无须类型转换 */
 console.log(result2.content);
}

这里需要理解的是,上面添加的函数类型仅作为 TypeScript 在编译时使用的,它不是真的实现像传统静态类型语言那样的重载,也不会改变编译后代码的输出。实际运行时仍然是不带重载的 JavaScript 版本。

编译后的代码

但这一点也不影响我们在 TypeScript 中使用这种假的重载。

可选参数

另一个 TypeScript 重载的场景。还是上面获取消息数据的方法,因为根据类型查找消息时,会返回同类型消息的一个数组。此时我们想加一个参数实现只返回结果中前几个数据,那么可以很方便地进行如下的改造:

function getMessage(id: number): Message | undefined;
+function getMessage(type: MessageType, count?: number): Message[];
+function getMessage(query: any, count = 10): any {
 if (typeof query === "number") {
  return data.find(message => message.id === query);
 } else {
+  return data.filter(message => message.type === query).splice(0, count);
 }
}

通过重载,这个新增的参数很容易实现只针对入参 MessageType 时,这样如果我们有如下的调用,会得到编译时的报错:

/** ? Argument of type '1' is not assignable to parameter of type 'MessageType' */
getMessage(1,10);

而非重载的版本是享受不到上面提到的类型优势的。

function getMessage(
 query: number | MessageType,
 count = 10
): Message[] | Message | undefined {
 if (typeof query === "number") {
  return data.find(message => message.id === query);
 } else {
  return data.filter(message => message.type === query).splice(0, count);
 }
}

/** ✅ ojbk, 不错报 */
getMessage(1, 10);

重载过程

TypeScript 重载的过程是,拿传入的参数和重载的方法签名列表中由上往下逐个匹配,直到找到一个完全匹配的函数签名,否则报错。所以推荐的做法是将签名更加具体的重载放上面,不那么具体的放后面。

/** ✅*/
function getMessage(type: MessageType, count?: number): Message[];
function getMessage(id: number): Message | undefined;

/** ?*/
function getMessage(id: number): Message | undefined;
function getMessage(type: MessageType, count?: number): Message[];

像上面示例中正确做法这样,如果说入参个数只有一个,那可以直接跳过第一个函数签名,无须做入参类型的判断。

相关资源

  • TypeScript Handbook - Functions - Overloads
  • Typescript method overloading

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
你可能不再需要JQUERY
Mar 09 Javascript
JavaScript TO HTML 转换
Jun 26 Javascript
jquery+ashx无刷新GridView数据显示插件(实现分页、排序、过滤功能)
Apr 25 Javascript
基于jquery实现的类似百度搜索的输入框自动完成功能
Aug 23 Javascript
关于jQuery object and DOM element
Apr 15 Javascript
jquery统计复选框选中示例
Nov 05 Javascript
jQuery的live()方法对hover事件的处理示例
Feb 27 Javascript
javascript获取元素偏移量的方法有哪些
Jun 24 Javascript
浅谈javascript中自定义模版
Jan 29 Javascript
bootstrap datetimepicker日期插件超详细使用方法介绍
Feb 23 Javascript
微信小程序实现自定义picker选择器弹窗内容
May 26 Javascript
JS+H5 Canvas实现时钟效果
Jul 20 Javascript
vue-cli 3.x配置跨域代理的实现方法
Apr 12 #Javascript
解决微信小程序调用moveToLocation失效问题【超简单】
Apr 12 #Javascript
详解Bootstrap 学习(一)入门
Apr 12 #Javascript
vue组件中iview的modal组件爬坑问题之modal的显示与否应该是使用v-show
Apr 12 #Javascript
vue+echarts实现可拖动节点的折线图(支持拖动方向和上下限的设置)
Apr 12 #Javascript
详解Vue中使用插槽(slot)、聚类插槽
Apr 12 #Javascript
JS实现li标签的删除
Apr 12 #Javascript
You might like
php数组函数序列之array_unshift() 在数组开头插入一个或多个元素
2011/11/07 PHP
浅谈Eclipse PDT调试PHP程序
2014/06/09 PHP
PHP中使用gettext解决国际化问题的例子(i18n)
2014/06/13 PHP
php将数组转换成csv格式文件输出的方法
2015/03/14 PHP
PHP实现可自定义样式的分页类
2016/03/29 PHP
为你的 Laravel 验证器加上多验证场景的实现
2020/04/07 PHP
关于javascript document.createDocumentFragment()
2009/04/04 Javascript
前淘宝前端开发工程师阿当的PPT中有JS技术理念问题
2010/01/15 Javascript
js chrome浏览器判断代码
2010/03/28 Javascript
ASP 过滤数组重复数据函数(加强版)
2010/05/31 Javascript
三种带箭头提示框总结实例
2016/06/14 Javascript
js利用appendChild对标签进行排序的实现方法
2016/10/16 Javascript
利用JS轻松实现获取表单数据
2016/12/06 Javascript
jQuery EasyUI结合zTree树形结构制作web页面
2017/09/01 jQuery
jQuery选择器之子元素过滤选择器
2017/09/28 jQuery
vue2.0+ 从插件开发到npm发布的示例代码
2018/04/28 Javascript
微信小程序HTTP接口请求封装代码实例
2019/09/05 Javascript
编写v-for循环的技巧汇总
2020/12/01 Javascript
Vite和Vue CLI的优劣
2021/01/30 Vue.js
Python学习资料
2007/02/08 Python
Python实现的手机号归属地相关信息查询功能示例
2017/06/08 Python
简单了解python gevent 协程使用及作用
2019/07/22 Python
python tkinter实现屏保程序
2019/07/30 Python
Python加密模块的hashlib,hmac模块使用解析
2020/01/02 Python
在jupyter notebook 添加 conda 环境的操作详解
2020/04/10 Python
python实现视频压缩功能
2020/12/18 Python
CSS3实现swap交换动画
2016/01/19 HTML / CSS
美国在线纱线商店:Darn Good Yarn
2019/03/20 全球购物
医学实习生自我鉴定
2013/12/12 职场文书
花店创业计划书范文
2014/02/07 职场文书
党员岗位承诺书
2014/03/25 职场文书
公证处委托书
2015/01/28 职场文书
2016教师暑期培训学习心得体会
2016/01/09 职场文书
导游词之江苏溱潼古镇
2019/11/27 职场文书
MyBatis 动态SQL全面详解
2021/10/05 MySQL
Python常遇到的错误和异常
2021/11/02 Python