TypeScript(三)类型缩小、函数类型、this、函数重载

本文整理来自深入Vue3+TypeScript技术栈-coderwhy大神新课,只作为个人笔记记录使用,请大家多支持王红元老师。

类型缩小

什么是类型缩小呢?
类型缩小的英文是 Type Narrowing,我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径,在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为缩小,而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards)。

常见的类型保护有如下几种:

  • typeof
  • 平等缩小(比如===、!==)
  • instanceof
  • in
  • 等等...

typeof

在 TypeScript 中,检查返回的值typeof是一种类型保护,因为我们可以根据不同的值进行不同的编码。

平等缩小

我们可以使用switch或者相等的一些运算符来表达相等性(比如===, !==, ==, != )。

instanceof

JavaScript 有一个运算符来检查一个值是否是另一个值的“实例”。

class Student {
  studying() {}
}

class Teacher {
  teaching() {}
}

function work(p: Student | Teacher) {
  if (p instanceof Student) {
    p.studying()
  } else {
    p.teaching()
  }
}

const stu = new Student()
work(stu)

in

Javascript 有一个in运算符,用于确定对象是否具有带名称的属性,如果指定的属性在指定的对象或其原型链中,则in运算符返回true。

//定义对象类型别名
type Fish = {
  swimming: () => void
}

type Dog = {
  running: () => void
}

function walk(animal: Fish | Dog) {
  if ('swimming' in animal) {
    animal.swimming()
  } else {
    animal.running()
  }
}

const fish: Fish = {
  swimming() {
    console.log("swimming")
  }
}

walk(fish)

TypeScript函数类型

在JavaScript开发中,函数是重要的组成部分,并且函数可以作为一等公民(可以作为参数,也可以作为返回值进行传递)。那么在使用函数的过程中,函数是否也可以有自己的类型呢?我们可以编写函数类型的表达式(Function Type Expressions)来表示函数类型。

// 定义函数
function foo() {}

// 定义函数类型
type FooFnType = () => void
// 函数作为参数
function bar(fn: FooFnType) {
  fn()
}
// 调用函数
bar(foo)
// 定义函数类型
type AddFnType = (num1: number, num2: number) => number
// 明确写出类型注解
const add: AddFnType = (a1: number, a2: number) => {
  return a1 + a2
}
// calc函数有三个参数,前两个参数是number,第三个参数是一个函数,再将前两个参数传入到第三个函数参数中,然后将函数的结果返回
function calc(n1: number, n2: number, fn: (num1: number, num2: number) => number) {
  return fn(n1, n2)
}

//调用
const result1 = calc(20, 30, function(a1, a2) {
  return a1 + a2
})
console.log(result1)

//调用
const result2 = calc(20, 30, function(a1, a2) {
  return a1 * a2
})
console.log(result2)

在上面的语法中 (num1: number, num2: number) => number,代表的就是一个函数类型。这个函数接收两个参数:num1和num2,并且都是number类型,并且返回一个number。在某些语言中,可能参数名称num1和num2可以省略,但是TypeScript是不可以的:

参数的可选类型

我们可以指定某个参数是可选的:

这个时候参数x依然是有类型的,其实就是联合类型:number | undefined,如下:

另外可选类型要在必传参数的后面,否则会报错:

默认参数

从ES6开始,JavaScript是支持默认参数的,TypeScript也是支持默认参数的:

这时候y的类型其实就是 number | undefined 联合类型。

有默认值的参数一般写在后面,以防有歧义,一般参数的顺序是:必传参数 - 有默认值的参数 - 可选参数。

剩余参数

从ES6开始,JavaScript也支持剩余参数,剩余参数语法允许我们将一个不定数量的参数放到一个数组中。

function sum(initalNum: number, ...nums: number[]) {
  let total = initalNum
  for (const num of nums) {
    total += num
  }
  return total
}

console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))

可推导的this类型

this是JavaScript中一个比较难以理解和把握的知识点,coderwhy在公众号也有一篇文章专门讲解this:https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA

因为this在不同的情况下会绑定不同的值,所以对于它的类型就更难把握了,那么,TypeScript是如何处理this呢?我们先来看一个例子:

上面的代码是可以正常运行的,也就是TypeScript在编译时,认为我们的this是可以正确去使用的,TypeScript认为函数 sayHello 有一个对应的this的外部对象 info,所以在使用时,就会把this当做该info对象。

不确定的this类型

但是对于某些情况来说,我们并不知道this到底是什么?

这段代码运行会报错。这里我们再次强调一下,TypeScript进行类型检测的目的是让我们的代码更加的安全,所以这里对于 sayHello 的调用来说,我们虽然将其放到了info中,通过info去调用,this依然是指向info对象的,但是对于TypeScript编译器来说,这个代码是非常不安全的,因为我们也有可能直接调用函数,或者通过别的对象来调用函数。

指定this的类型

这个时候,通常TypeScript会要求我们明确的指定this的类型:

type ThisType = { name: string };

// 指定this的类型
function eating(this: ThisType, message: string) {
  console.log(this.name + " eating", message);
}

const info = {
  name: "why",
  eating: eating,
};

// 隐式绑定
info.eating("哈哈哈");

// 显示绑定
eating.call({name: "kobe"}, "呵呵呵")
eating.apply({name: "james"}, ["嘿嘿嘿"])

函数的重载

在TypeScript中,如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加,应该如何编写呢?我们可能会这样来编写,但是其实是错误的:

如果我们通过联合类型:

function add(a1: number | string, a2: number | string) {
  if (typeof a1 === "number" && typeof a2 === "number") {
    return a1 + a2
  } else if (typeof a1 === "string" && typeof a2 === "string") {
    return a1 + a2
  }
  // return a1 + a2;
}

add(10, 20)

通过联合类型有两个缺点:

  1. 进行很多的逻辑判断(类型缩小)
  2. 返回值的类型依然是不能确定

那么这个代码应该如何去编写呢?
在TypeScript中,我们可以去编写不同的重载签名(overload signatures)来表示函数可以以不同的方式进行调用,一般是编写两个或者以上的重载签名,再去编写一个通用的函数以及实现。

Swift中的函数重载:函数名相同,参数个数不同 或 参数类型不同 或 参数标签不同。
TypeScript中的函数重载:函数名相同,但是参数不同的几个函数,就是函数的重载。

sum函数的重载

比如我们对sum函数进行重构,在我们调用sum的时候,它会根据我们传入的参数类型来决定执行函数体时,到底执行哪一个函数的重载签名。

// 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载
function add(num1: number, num2: number): number; // 没函数体
function add(num1: string, num2: string): string; // 没函数体

// 有实现体的函数,不能直接被调用
function add(num1: any, num2: any): any {
  if (typeof num1 === 'string' && typeof num2 === 'string') {
    return num1.length + num2.length
  }
  return num1 + num2
}

const result = add(20, 30) // 匹配第一个函数,最后会把第三个函数的实现当做函数体来执行
const result2 = add("abc", "cba") // 匹配第二个函数,最后会把第三个函数的实现当做函数体来执行
console.log(result) // 50
console.log(result2) //6

但是注意,有函数体的函数,是不能直接被调用的,所以上面第三个函数不能直接被调用,否则报错:

// 在函数的重载中, 实现函数是不能直接被调用的
add({name: "why"}, {age: 18})

TypeScript的函数重载,不能有函数体,并且有函数体的函数不能直接被调用,只能先匹配没函数体的函数,最后再调用有函数体的函数的实现,TypeScript的函数重载和其他语言的函数重载是有点不一样。

函数重载练习

我们现在有一个需求:定义一个函数,可以传入字符串或者数组,获取它们的长度。这里有两种实现方案。
方案一:使用联合类型来实现;
方案二:实现函数重载来实现;

这里肯定是使用联合类型来实现更方便,我们优先选择使用联合类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容