一文读懂TS的函数重载,方法重载,构造器重载
这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
重载的理解
用于实现不同参数输入并且对应不同参数输出的函数,在前面定义多个重载签名,一个实现签名,一个函数体构造,重载签名主要是精确显示函数的输入输出,实现签名主要是将所有的输入输出类型做一个全量定义,防止TS编译报错,函数体就是整个整个函数实现的全部逻辑。
函数重载
应用例子:一个班级有很多学生,老师要查询学生的用户信息,如果是输入数字就通过排名(id)去匹配,输入的是字符串就通过分数(grades)去匹配,用TS模拟这种情况。 首先定义用户类型
type User={
id:number,
name:string,
age:number,
grades:number
}
班级用户信息数据
const userList:User[]=[
{id:1,name:"小明",age:20,grades:'98'},
{id:2,name:"小明",age:20,grades:'98'},
{id:3,name:"小明",age:20,grades:'98'},
{id:4,name:"小明",age:20,grades:'98'}
]
1.普通实现思路
通过联合类型来做输入定义,输出定义。输入number是(id)是唯一的名次,使用find;输入string是(grades)可能有重复的,使用filter。
function getUserInfo(value:number|string):User|User[]{
if(typeof value==='number'){
return userList.find(item=>item.id===value)
}else{
return userList.filter(item=>item.grades===value)
}
}
这样定义TS会报一个异常
原因切到底层find方法源码,发现是find可能返回的undefined类型
解决方式很简单,返回类型再联合一个undefined即可解决。
缺点:缺点很明确,都是通过联合类型输入输出,明明知道number输入一定返回User,string输入一定返回User[],这样返回不明确输入什么返回什么,很多时候我们看方法的定义的时候就想看输入啥返回啥,更好的判定类型,为了解决这个函数重载出现了
2.函数重载实现思路
TS的函数重载主要分为多个重载签名+实现签名+函数体
简单理解来看实现签名主要是之前普通实现思路的情况,TS编译的时候检查所有类型和函数体中的对比检查是否存在。实现签名和函数体检查通过后,执行函数的时候实际上是某个重载签名+函数体,跳过了实现签名。
function getUserInfo(value:number):User|undefined
function getUserInfo(value:string):User[]
function getUserInfo(value:number|string):User|User[]|undefined{
if(typeof value==='number'){
return userList.find(item=>item.id===value)
}else{
return userList.filter(item=>item.grades===value)
}
}
通过鼠标点击实际调用方法getUserInfo(2)
,command+鼠标右键,他会自动解析跳转到number这个重载签名上面去,很直观的知道如何调用的。
需求变更: 现在查询相同分数太多,老师输入string想添加一个变量count用于控制查询数量。
修改步骤,第二个重载签名和实现签名添加count定义,函数体书写count逻辑,但是TS报出一下错,意思是实现签名传了两个值,第一个重载签名却只有一个值,那么只有在实现签名增加一个count默认值即可。
具体实现源码如下
function getUserInfo(value:number):User|undefined
function getUserInfo(value:string,count:number):User[]
function getUserInfo(value:number|string,count:number=1):User|User[]|undefined{
if(typeof value==='number'){
return userList.find(item=>item.id===value)
}else{
return userList.filter(item=>item.grades===value).slice(0,count)
}
}
getUserInfo('98',3)
方法重载
方法重载和函数重载其实差不多,还是方法重载是放在类里面的 应用例子:简单封装一个数组,使数组更加好用,通过index删除返回index,通过object删除返回object
class ArrayEN {
constructor(public arr: object[]) {}
get(Index: number) {
return this.arr[Index];
}
delete(value: number): number;
delete(value: object): object;
delete(value: number | object): number | object {
this.arr = this.arr.filter((item, index) => {
if (typeof value === "number") {
return value !== index;
} else {
return value !== item;
}
});
return value;
}
}
构造器重载
先来理解构造器constructor的原理,构造器是没有返回值的,他会隐式返回一个this,这个this会分配给new对象的左边的变量,至此所有的this都是指向的当前正在使用的对象。
构造器重载和函数重载使基本相同,主要区别是:TS 类构造器重载签名和实现签名都不需要管理返回值,TS 构造器是在对象创建出来之后,但是还没有赋值给对象变量之前被执行,一般用来给对象属性赋值。 应用例子:现在要求算一个图片的面积,这个图形可能是传参可以是对象也可能是长宽
interface OJType{
width?:number,
height?:number
}
class Graph{
public width:number;
public height:number;
constructor(width?:number,height?:number)
constructor(side?:OJType)
constructor(v1:any,v2?:any){
if(typeof v1==='object'){
this.width=v1.width;
this.height=v1.height
}else{
this.width=v1;
this.height=v2;
}
}
getArea(){
const {width,height}=this;
return width*height
}
}
const g=new Graph(10,10);
console.log(g.getArea())
主要实现思路就是通过构造器将类型定义出来,首先是带有长宽对象的类型,这里才用接口的形式定义,然后就是width,heihgt的number类型,最后简单的通过typeod判断对象类型然后进行赋值,综上发现不管是函数重载,方法重载,构造器重载都是异曲同工之妙,只是需要注意使用场景就行,在vue3的源码很多地方都使用到了,所以有很多场景都是可以用到的,后面会继续深挖TS的一些内容.
转载自:https://juejin.cn/post/7055668560965681182