TypeScript中的⽅法重载详解
前⾔
⽅法重载(overload)在传统的静态类型语⾔中是很常见的。JavaScript 作为动态语⾔,是没有重载这⼀说的。⼀是它的参数没有类型的区分,⼆是对参数个数也没有检查。虽然语⾔层⾯⽆法⾃动进⾏重载,但借助其动态的特性,我们可以在代码中⼿动检查⼊参的类型,或者通过 arguments 获取到参数个数,从⽽实现根据不同的⼊参做不同的操作。
⽐如有⼀个获取聊天消息的⽅法,根据传⼊的参数从数组中查数据。如果⼊参为数字,则认为是 id,然后从数据源中对应 id 的数据并返回,否则当成类型,返回这⼀类型的消息。
function getMessage(query) {
if (typeof query === "nunber") {
return data.find(message => message.id === query);
} else {
return data.filter(message => pe === 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 => pe === query);
}
}
这样做⼀是类型书写上⽐较丑陋,⼆是没有发挥出 TypeScript 类型检查的优势,这⾥我们是可以根据⼊参的类型明确知道返回的类型的,即如果传⼊的是 id,返回的是单个数据或undefined,如果是根据类型查,返回的是数组。⽽现在调⽤⽅法后,得到的类型太过宽泛,这和使⽤ any 做为返回没多⼤差别。
函数返回类型不够紧凑
因为类型的不明朗,返回的结果都不能直接操作,需要进⾏类型转换后才能继续。
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 => pe === query);
}
}
这样改造后,我们在调⽤的时候直接就会有重载的提⽰。
实现 TypeScript 的重载后调⽤时的⾃动提⽰
并且得到的结果类型是重载⽅法中指定的⼊参与返回的组合,在对结果进⾏使⽤时,⽆须再进⾏类型转换。
const result1 = getMessage("audio");
/** ⽆须类型转换 */
console.log(result1.length);
const result2 = getMessage(1);
if (result2) {
/** ⽆须类型转换 */
console.t);
}
这⾥需要理解的是,上⾯添加的函数类型仅作为 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 {
typescript 字符串转数组+ return data.filter(message => pe === 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 => pe === 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
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,谢谢⼤家对的⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论