TypeScript_2_typescript类型补充
目录
TypeScript中的类型补充
函数的参数类型
-
函数是JavaScript非常重要的组成部分,TypeScript允许我们指定函数的参数和返回值的类型。
-
参数的类型注解
-
声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型:
-
函数的返回值类型
-
我们也可以添加返回值的类型注解,这个注解出现在函数列表的后面:
-
和变量的类型注解一样,我们通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的 返回类型:
- 某些第三方库处于方便理解,会明确指定返回类型,但是这个看个人喜好;
匿名函数的参数
-
匿名函数与函数声明会有一些不同:
-
当一个函数出现在TypeScript可以确定该函数会被如何调用的地方时;
-
该函数的参数会自动指定类型;
-
-
我们并没有指定item的类型,但是item是一个string类型:
- 这是因为TypeScript会根据forEach函数的类型以及数组的类型推断出item的类型;
- 这个过程称之为上下文类型(contextual typing),因为函数执行的上下文可以帮助确定参数和返回值的类型;
对象类型
-
如果我们希望限定一个函数接受的参数是一个对象,这个时候要如何限定呢?
-
我们可以使用对象类型;
-
-
在这里我们使用了一个对象来作为类型:
- 在对象我们可以添加属性,并且告知TypeScript该属性需要是什么类型;
- 属性之间可以使用 , 或者 ; 来分割,最后一个分隔符是可选的;
- 每个属性的类型部分也是可选的,如果不指定,那么就是any类型;
可选类型
-
对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个?:
联合类型
-
TypeScript的类型系统允许我们使用多种运算符,从现有类型中构建新类型。
-
我们来使用第一种组合类型的方法:联合类型(Union Type)
- 联合类型是由两个或者多个其他类型组成的类型;
- 表示可以是这些类型中的任何一个值;
- 联合类型中的每一个类型被称之为联合成员(union’s members);
使用联合类型
-
传入给一个联合类型的值是非常简单的:只要保证是联合类型中的某一个类型的值即可
-
但是我们拿到这个值之后,我们应该如何使用它呢?因为它可能是任何一种类型。
-
比如我们拿到的值可能是string或者number,我们就不能对其调用string上的一些方法;
-
-
那么我们怎么处理这样的问题呢?
-
我们需要使用缩小(narrow)联合(后续我们还会专门讲解缩小相关的功能);
-
TypeScript可以根据我们缩小的代码结构,推断出更加具体的类型;
-
可选类型补充
- 其实上,可选类型可以看做是 类型 和 undefined 的联合类型:
类型别名
- 在前面,我们通过在类型注解中编写 对象类型 和 联合类型,但是当我们想要多次在其他地方使用时,就要编写多 次。
- 比如我们可以给对象类型起一个别名:
类型断言as
-
有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)。
-
比如我们通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它 具体的类型:
-
-
TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换:
非空类型断言!
-
当我们编写下面的代码时,在执行ts的编译阶段会报错:
-
这是因为传入的message有可能是为undefined的,这个时候是不能执行方法的;
-
-
但是,我们确定传入的参数是有值的,这个时候我们可以使用非空类型断言:
-
非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测;
-
可选链的使用
-
可选链事实上并不是TypeScript独有的特性,它是ES11(ES2020)中增加的特性:
-
可选链使用可选链操作符 ?.;
-
它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行;
-
虽然可选链操作是ECMAScript提出的特性,但是和TypeScript一起使用更版本;
-
??和!!的作用
-
有时候我们还会看到 !! 和 ?? 操作符,这些都是做什么的呢?
-
!!操作符:
-
将一个其他类型转换成boolean类型;
-
类似于Boolean(变量)的方式;
-
-
??操作符:
-
它是ES11增加的新特性;
-
空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数, 否则返回左侧操作数;
-
字面量类型
-
除了前面我们所讲过的类型之外,也可以使用字面量类型(literal types):
-
那么这样做有什么意义呢?
-
默认情况下这么做是没有太大的意义的,但是我们可以将多个类型联合在一起;
-
字面量推理
- 我们来看下面的代码:
- 这是因为我们的对象再进行字面量推理的时候,info其实是一个 {url: string, method: string},所以我们没办法将 一个 string 赋值给一个 字面量 类型。
类型缩小
- 什么是类型缩小呢?
- 类型缩小的英文是 Type Narrowing;
- 我们可以通过类似于 typeof padding === “number” 的判断语句,来改变TypeScript的执行路径;
- 在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小;
- 而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards);
- 常见的类型保护有如下几种:
- typeof
- 平等缩小(比如===、!==)
- instanceof
- in
- 等等…
typeof
- 在TypeScript中,检查返回的值typeof是一种类型保护:应为TypeScript对如何typeof操作不同的值进行编码
平等缩小
- 我们可以使用Switchh或者相等的一些运算符来表达相等性(比如===,!,,!=)
instanceof
- JavaScript有一个运算符来检查一个值是否是另一个值的实例;
in
-
JavaScript有一个运算符,用于缺点对象是否具有带名称的属性:in运算符
-
如果指定的属性在指定的对象或其原型链中,则in运算符返回true
-
TypeScript中的函数类型
-
在JavaScript开发中,函数是重要的组成部分,并且函数可以作为一等公民(可以作为参数,也可以作为返回值进行传递)。
-
那么在使用函数的过程中,函数是否也可以有自己的类型呢?
-
我们可以编写函数类型的表达式(FunctionTypeExpressions),来表示函数类型;
-
TypeScript函数类型解析
-
在上面的语法中(num1:number,num2:number)=>void,代表的就是一个函数类型:
- 接收两个参数的函数:num1和num2,并且都是number类型;
- 并且这个函数是没有返回值的,所以是void;
-
在某些语言中,可能参数名称num1和num2是可以省略,但是TypeScript是不可以的:
参数的可选类型
-
可以指定某个参数是可选的:
-
这个时候这个参数x依然是有类型的,它是什么类型呢?number|undefined
-
另外可选类型需要在必传参数的后面:
默认参数
-
从ES6开始,JavaScript是支持默认参数的,TypeScript也是支持默认参数的:
-
这个时候y的类型其实是undefined和number类型的联合。
剩余参数.
-
从ES6开始,JavaScript也支持剩余参数,剩余参数语法允许我们将一个不定数量的参数放到一个数组中。
可推导的this类型
-
this是JavaScript中一个比较难以理解和把握的知识点:
- 我在公众号也有一篇文章专门讲解this:https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA;
- 因为this在不同的情况下会绑定不同的值,所以对于它的类型就更难把握了;
-
那么,TypeScript是如何处理this呢?我们先来看一个例子:
-
上面的代码是可以正常运行的,也就是TypeScript在编译时,认为我们的this是可以正确去使用的:
- TypeScript认为函数sayHello有一个对应的this的外部对象info,所以在使用时,就会把this当做该对象。
不确定的this类型
-
但是对于某些情况来说,我们并不知道this到底是什么?
-
这段代码运行会报错的:
- 这里我们再次强调一下,TypeScript进行类型检测的目的是让我们的代码更加的安全;
- 所以这里对于sayHello的调用来说,我们虽然将其放到了info中,通过info去调用,this依然是指向info对象的;
- 但是对于TypeScript编译器来说,这个代码是非常不安全的,因为我们也有可能直接调用函数,或者通过别的对象来调用函数;
指定this的类型
-
这个时候,通常TypeScript会要求我们明确的指定this的类型:
函数的重载
-
在TypeScript中,如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加,应该如何编写呢?
-
我们可能会这样来编写,但是其实是错误的:
-
那么这个代码应该如何去编写呢?
- 在TypeScript中,我们可以去编写不同的重载签名(overloadsignatures)来表示函数可以以不同的方式进行调用;
- 一般是编写两个或者以上的重载签名,再去编写一个通用的函数以及实现;
sum函数的重载
-
比如我们对sum函数进行重构:
-
在我们调用sum的时候,它会根据我们传入的参数类型来决定执行函数体时,到底执行哪一个函数的重载签名;
-
-
但是注意,有实现提的函数,是不能直接被调用的:
联合类型和重载
-
我们现在有一个需求:定义一个函数,可以传入字符串或者数组,获取它们的长度。
-
这里有两种实现方案:
-
方案一:使用联合类型来实现;
-
方案二:实现函数重载来实现;
-
-
在开发中我们选择使用哪一种呢?
- 在可能的情况下,尽量选择使用联合类型来实现;