TypeScript基础(一)- 常见类型和类型收窄

623 阅读11分钟

本文参考:
ts.yayujs.com/
www.typescriptlang.org/docs/handbo…

1. TS基本特性

1.1 静态类型检查

在代码运行之前检查 应有的结构和行为

静态类型检查

1.2 非异常失败

非异常失败是指一些语法错误的提示,主要包含如下几种

  • 对象属性不存在的时候调用
  • 拼写错误
  • 函数未被调用
  • 基本逻辑错误

1.2.1 对象属性不存在的时候调用

非异常失败-属性不存在

1.2.2 拼写错误

非异常失败-拼写错误

1.2.3 函数未被调用

非异常失败-函数未被调用

1.2.4 基本逻辑错误

非异常失败-逻辑错误

1.3 类型工具

使用 typeScript 的时候,会自动给我们提供如下功能

  • 获取变量属性:ts会根据变量类型自动给出相应属性的使用提示
  • 代码补齐
  • 快速修复功能

1.4 tsc: TypeScript编译器

安装:npm i -g typescript

编译:tsc fileName

  • 编译之后会自动在同级目录生成一个同文件名的js文件
  • 如果编译过程中有报错,会在控制台提示出来

tsc编译器

1.5 报错不产生编译文件

在上面例子中,即使使用 tsc 编译报错,编译后的文件依然会生成

有时候如果需要更严格的控制,可以在编译命令中增加 --noEmitOnError 命令来禁止报错时编译文件的生成

禁止编译报错生成文件:tsc --noEmitOnError filename

1.6 ES编译降级

使用 tsc 命令编译 ts 的时候,默认会被编译成 ES3(EcmaScript比较老的一个版本) 语法的 js,我们可以使用 target 命令将其编译成一些比较新的版本

编译成ES5:tsc --target es2015 filename

1.7 严格模式

如果你希望 ts 尽可能多的检查代码,可以通过开启如下配置,使用更严格的检查模式

1.7.1 规避any

noImplicitAny: 当类型被隐形推断为 any 的时候会报错

1.7.2 规避 null,undefined

strictNullChecks:该配置让我们更明确的处理 nullundefined ,避免忘记处理 nullundefined 从而导致的bug

2. 常见类型

2.1 原始类型

typeScript中原始类型有三种:string, number, boolean

原始类型

2.2 数组

常见数组有 数字数组字符串数组

数组类型

2.3 any

在ts中,any 是一种特殊的类型,当你不希望编译器对变量的值进行检查的时候,可以使用 any(相当于js变量)

一般不推荐使用 any ,除非是刻意不希望编译器检查变量的时候,默认情况下编译器会根据变量值去推断变量类型,从而给出相应的检查和提示

any类型

2.4 函数

函数主要有两种类型的注解

  1. 参数类型注解

  • 当参数有了类型注解之后,Ts便会检查函数的实参

  • 即使没有添加参数类型注解,Ts依然会检查传入的参数数量是否正确

  1. 返回值类型注解

  • 返回值类型注解(非必要,TS会根据 return 语句推断函数的返回值类型)

函数注解

2.5 对象

  1. 定义一个对象类型,需要简单的列出它的属性和对应类型

function greet(obj: { name: string; age: number }){} greet({name: 'kobe', age: 18})

  1. 对象类型可选属性

  • 对象类型可以指定属性为可选的,只需要在属性名后面增加一个?

  • 在使用一个可选属性之前,需要检查一下是否为undefined

对象类型

2.6 联合类型

2.6.1 定义联合类型

联合类型: 由两个或更多个基础类型组成的组合, 表示值可以是其中的任意一种类型

定义联合类型

2.6.2 使用联合类型

Ts要求你使用联合类型的时候,需要对每一个可能的类型都是有效的,例如有一个 number | string 类型的变量,你不能使用只存在 string 类型变量上面的方法

  • 类型收窄:针对每种可能类型做判断,单该类型存在,再使用
  • 使用共用属性:每一种可能类型都存在的属性可以直接用

引用 mqyqingfeng 大佬的一个解释:你可能很奇怪,为什么联合类型只能使用这些类型属性的交集,让我们举个例子,现在有两个房间,一个房间都是身高八尺戴帽子的人,另外一个房间则是会讲西班牙语戴帽子的人,合并这两个房间后,我们唯一知道的事情是:每一个人都戴着帽子。

使用联合类型

2.7 类型别名

有时候,当一种类型被多次引用的时候,我们希望通过一个单独的名字去使用它,这时候类型别名就出现了

类型别名:一个可以指代任意类型的名字

类型别名

2.8 接口

接口和类型别名相似,大部分时候可以任意使用。

两者的关键区别在于接口可以扩展,但是类型别名无法添加新的属性

类型别名和接口

2.9 类型别名和接口的区别

  • 类型别名不可重复声明,但是接口可以,重复声明的接口将会合并(不能覆盖前面声明的类型)
  • 类型别名只能通过交集去扩展类型,但是接口可以通过继承去扩展类型
  • 报错信息中,接口名一定会出现,而类型名是有可能会出现

类型别名和接口的区别

2.10 字面量类型

除了常见的 stringnumber 类型之外,我们还可以声明更为具体的数字和字符串类型

即该值只能是指定的字面量类型之一,否则报错

字面量类型

2.11 null 和 undefined

javascript 中,有两个原始类型的值用来表示值为 空 或者 未初始化,他们分别是 nullundefined

在 TypeScript 中也同样对应这两个类型,但是他们的行为取决于是否开启了strictNullChecks 配置

strictNullChecks关闭

strickNullChecks 关闭的时候,如果一个值是 Null 或者 undefined,它们任可以被正常的访问,或者赋值给任意类型的属性,但是这种检查的缺少,正是导致bug的主要源头,所以始终推荐开发者开启 strictNullChecks 选项

strictNullChecks开启

strictNullChecks 选项开启的时候,如果一个值是 nullundefined,使用它之前需要先检查一下是否为 undefined

strictNullChecks开关

2.12 非空类型断言

TypeScript 提供一个特殊的语法,在不做检查的情况下,从类型中移除 nullundefined

这就是在任意类型后面加上!,表示这是一个非空类型的断言,表示它的值不可能是 nullundefined

注意:当你明确知道这个值不可能是 null 或 undefined 的时候才使用 !

非空断言

总结

  • 相对于 JS 来说,TS 给开发者提供了更为严格的静态类型检查,更智能的类型提示工具,更好的异常失败提醒
  • 所有的 .ts 文件最终都会被编译成 .js 文件,我们可以通过 TypeScript 编译器手动编译某个 .ts 文件
  • tsc --noEmitOnError filename 命令可以用来预防编译报错文件的生成
  • Ts 常见类型有 number, string, boolean , 数组, 对象, any 这几种,
  • 字面量类型可以指定具体的值作为某个变量的取值,联合类型可以通过 | 的方式将集中不通类型的取值指定到同一个变量上面
  • 建议始终在项目中开启 strictNullChecks,使用 nullundefined 之前进行类型收窄的判定

3.类型收窄

3.1 类型收窄概念

在前面我们介绍联合类型的时候提到过 类型收窄 这个概念

简单来讲就是 当一个值可能拥有多种类型的时候,我们需要使用一定的类型判断,将它推导为更精确类型的这个过程,我们称之为收窄

因为在 TS 中,每一个值使用的时候,编译器都需要知道它确定的值类型

错误示范

类型收窄错误示范

如果你能一眼就看出问题,那说明联合类型那里你学的不错,如果你看不出来,也没关系,我们一起再回顾一下

使用联合类型的时候,你不能只使用存在某一个类型上面的方法,如果你不愿意做 类型收窄,那么你就只能使用所有可能类型都存在的公用方法 例: 现有const id = number | string

如果不通过某些类型判断方法去确定 id 具体类型,那么我们就只能使用 stringnumber 都存在的方法或属性

正确示范

类型收窄正确示范

3.2 类型保护

类型保护用一句话说就是:在使用某个不确定类型的值之前,对该类型做一定的判断,确定是该类型的时候才去使用

最常用的类型保护方法是:typeof

  • string
  • number
  • bigInt
  • boolean
  • symbol
  • undefined
  • object
  • function

⚠️注意:typeof只能准确检验出基本数据类型

3.3 类型收窄的方式

3.3.1 真值收窄

通俗来说就是在条件语句 if 中使用任何表达式,例如用逻辑运算符 &&, ||, ! 来判断是否是某个特定类型

示例1

真值收窄-基本示例

示例2

真值收窄-基本示例2

示例3

真值收窄-基本示例3

3.3.2 等值收窄

通过 switch 语句或 ===, !==, == , != 去收窄类型

示例1

等值收窄-基本示例1

示例2

⚠️注意:当使用 someValue != null 来判断的时候,null 和 undefined 都返回 true

等值收窄-基本示例2

3.3.3 in操作符

在 JavaScript 中,in 操作符可以用来判断某个对象是否拥有某个属性名,Ts 也可以通过这个收窄类型

in操作符收窄-基本示例1

3.3.4 instanceof收窄

前面我们说过,typeof 可以用来收窄基本数据类型,那么相对的, instanceof 用来收窄引用数据类型

instanceof收窄-基本示例1

3.3.5 赋值语句收窄

Ts 可以根据赋值语句右边的值,正确收窄左侧的变量类型

赋值收窄-基本示例1

3.4 控制流分析

前面介绍了 TS 中一些基础的类型收窄用法,现在我们来看下在实际的逻辑语句中类型保护的例子

概念

TS 通过代码分析,在过滤掉前面的类型收窄之后,剩余的部分就会从 原可能的类型中删掉,所以到逻辑的最后,遗留的值就是未做明确类型收窄的值类型。这种基于 可达性 的代码分析就叫做控制流分析

控制流分析

3.5 类型判断式

类型判断式是用来是我们的类型收窄更加灵活方便的,通过类型判断式我们可以自定义一个类型保护

语法:parameterName is Type

使用示例

类型判断式-自定义类型保护

3.6 可辨别联合

可辨别联合,通俗理解就是 在某个联合类型中,可以通过特定的共有属性或方法,来达到识别某种类型的目的

可辨别联合属性

3.7 never类型

never定义

TS 中有一个 never 类型用来表示一个不可能存在的值

never 类型可以赋值给任何类型,但是,没有任何类型可以赋值给 never(除了never自身)

定义never

3.8 穷尽检查

穷尽检查的概念: 当我们想通过类型收窄判断所有的类型,但是又担心遗漏的时候,可以通过 never 做一个类型穷尽检查,因为任何类型都不能被赋值给 never,这时候就会产生一个编译错误,以达到提示开发者的目的

穷尽检查

总结

总得来说,这一章主要介绍了 TS类型收窄的基本概念,以及在类型收窄的基础上我们可以去使用的类型保护方法,所有的类型方法都是源于 JS 的基础类型判定语法,主要有

  • 真值收窄(逻辑运算)
  • 等值收窄(===, !==, ==, !=
  • in 操作符 结合联合类型使用 ('protoName' in Obj)
  • Instanceof收窄(判定引用类型)
  • 赋值收窄 (通过复制语句限定变量类型)

这几种类型收窄的方法,在实际实践过程中可根据具体情况结合使用

其次,通过控制流分析,在某些场景下可以自动分析出变量的类型,不用我们主动对所有类型做收窄的判断

还有类型判断式 可以方便我们自定义类型保护的方法,可辨别联合 算不上一种特殊的方法,在我看来更多的像是组合代码逻辑的一种技巧,最后我们学习了一个 never 类型,主要用来做穷尽检查的,特别是在我们在 switch 中无法详细的陈列出所有的类型的时候。最后的最后记住never的特性:只能赋值给其它,不能被除 never 之外的任何类型赋值

🎉这篇就到这里了,小伙伴们,我们下期再见吧

👉下期预告:【函数,对象类型,泛型】

更多学习笔记和优质内容,请关注公众号:马小飞学前端

微信公众号推广.bmp