【前端47_TypeScript】为什么要用 TypeScript、类型系统、源码地址
为什么要用 TypeScript
先介绍两个概念哈:
动态类型语言:程序运行期间才做数据类型检查的语言,如:JavaScript
静态类型语言:程序编译期间做数据类型检查的语言,如:Java
静态类型语言的优点
- 程序编译阶段(配合 IDE、编辑器甚至可以在编码阶段)即可发现一些潜在错误,避免程序在生产环境运行了以后再出现错误
- 编码规范、有利于团队开发协作、也更有利于大型项目开发、项目重构
- 配合 IDE、编辑器提供更强大的代码智能提示/检查
- 代码即文档
这些优点都是基于类型系统的
类型系统
TypeScript
引入了类型系统
类型系统有以下这几个功能:
- 类型标注(定义、注解)
- 类型检测(检查)
类型标注
首先大致看一下怎么用的:
1 | // 基本类型 |
也就是在变量后加冒号,再加类型的声明
1 | var 变量名: 类型 = 值; |
类型检测
此时如果你试图给 string
类型的变量声明其他类型,ts
就会以报错的形式告诉你(要配合 VSCode
或者支持 TypeScript
的编辑器,才会有一些错误提示,这里就是红色的波浪了)。
如果此时进行编译,他也会报错,这肯定啊。
小结
从这里就可以知道,我们的类型标注,其实是给 vsCode(或者说是编译器) 看的,告诉编译器这个变量存储的是什么 类型的值,然后编译器才回去检测类型。
所有类型
类型的话有这么一坨,这个不太敢放在前面了,太多了会不会劝退很多人呢?,口区 。
- 基础类型
- 空和未定义类型
- 对象类型
- 数组类型
- 元组类型
- 枚举类型
- 无值类型
- Never 类型
- 任意类型
- 未知类型(Version3.0 Added)
基本类型
1 | var title: string = "hello Typescript"; // 字符串类型的标注 |
空、未定义类型
1 | // 空、未定义 |
有以下几个场景需要注意
场景 1:因为在
Null
和Undefined
这两种类型有且只有一个值,在标注一个变量为Null
和Undefined
类型,那就表示该变量不能修改成别的了
场景 2:默认情况下
null
和undefined
是所有类型的子类型。 就是说你可以把null
和undefined
赋值给其它类型的变量
场景 3:默认的值是
undefined
,默认的类型是any
场景 4:比较隐蔽的问题,因为
null
和undefined
都是其它类型的子类型,所以默认情况下会有一些隐藏的问题
比方说下面这个例子:
解决的办法就是在 tsconfig.json
添加 null 值严格检查
1 | "strictNullChecks": true // null 值严格检查,工作中可以添加 |
添加完之后就会报错了:
null
值的检测,还能够使代码更严谨
以下就是不够严谨的代码:
可以通过这样改,就可以避免给 null
元素赋值了
对象类型
内置对象类型
在 JavaScript
中,有许多的内置对象,比如:Object、Array、Date……,我们可以通过对象的 构造函数 或者 类 来进行标注
自定义对象类型
另外一种情况,许多时候,我们可能需要自定义结构的对象。这个时候,我们可以这样:
1 | let user: { username: string; age: number } = { |
如果你添加了没有声明的属性,他就会报错
如果要定义很多个对象,其结构都是统一的,一个个像如下图这样定义的话就很麻烦
解决的办法就是用接口
接口 interface
接口的使用,首先定义一个接口,然后再创建对象的时候使用它。
它的优点当然明显了,就是复用性高,但是他也有缺点,即:接口只能作为类型标注使用,不能作为具体值,它只是一种抽象的结构定义,并不是实体,没有具体功能实现
接口只存在 TypeScript
的编译阶段,编译后的 JavaScript
文件中是没有这个的。
类与构造函数
- 这个
Person
定义下来之后,是可以直接使用的,这个不像interface
- 并且同时定义了
new
出来的对象的类型,就是Person
类
优点
: 功能相对强大,定义实体的同时也定义了对应的类型
缺点
: 复杂,比如只想约束某个函数接收的参数结构,没有必要去定一个类,使用接口会更加简单
扩展:包装对象
字符串对象String
> 字符串 string
字符串有的东西,字符串对象是一定有的。
如果试图将对象类型赋值给简单类型,这样会丢失数据的。而反过来就不会丢失数据。
数组类型
定义数组有以下几种方法,可以看到也是有类型检测的
元组类型
如何向数组中添加类型不同的数据呢?这时候就引入了元组。
元组类似数组,但是存储的元素类型不必相同,但是需要注意:
- 初始化数据的个数以及对应位置标注类型必须一致
- 后期,越界数据必须是元组标注中的类型之一(标注越界数据可以不用对应顺序 - 联合类型)\
枚举类型
枚举的作用组织收集一组关联数据的方式,通过枚举我们可以给一组有关联意义的数据赋予一些友好的名字
注意事项:
- key 不能是数字
- value 可以是数字,称为 数字类型枚举,也可以是字符串,称为 字符串类型枚举,但不能是其它值,默认为数字:0
- 枚举值可以省略,如果省略,则:
- 第一个枚举值默认为:0
- 非第一个枚举值为上一个数字枚举值 + 1
- 如果前一个枚举值类型为字符串,则后续枚举项必须手动赋值
- 枚举值为只读(常量),初始化后不可修改
可以观察以下编译后的代码
无值类型
如果函数返回的是 void ,那么他就不能 return null
,然而 return undefined
是可以的
在
strictNullChecks
为false
的情况下,undefined
和null
都可以赋值给void
,但是当strictNullChecks
为true
的情况下,只有undefined
才可以赋值给void
Never 类型(不常用)
当一个函数永远不可能执行 return
的时候,返回的就是 never
,与 void 不同,void
是执行了 return
, 只是没有值,never
是不会执行 return
,比如抛出错误,导致函数终止执行
1 | function fn(): never { |
任意类型
- 一个变量申明未赋值且未标注类型的情况下,默认为
any
类型 - 任何类型值都可以赋值给
any
类型
注意:标注为 any
类型,也意味着放弃对该值的类型检测,同时放弃 IDE 的智能提示
想要避免这样的问题,可以在这里写个配置
之后就会在代码中报错了(如果不报错的话,重启一下项目试试,有的时候会不报错)
未知类型 (3.0 版本新增)
unknown
的用法跟 any
差不多,但是 unknown
比 any
更安全,更严格。
函数类型
主要就是返回值的类型,注意声明的位置