这是我参与「第四届青训营 」笔记创作活动的的第9天
为什么选择 TypeScript
了解一件事物先去了解它发展的历史,历史代表着它产生的含义和意义。
- 2012-10:微软发布了 TypeScript 第一个版本(0.8)
- 2014-10: Angular 发布了基于 TypeScript 的2.0版本
- 2015-04:微软发布了 Visual Studio Code
- 2016-05:@types/react 发布, TypeScript 可开发 React
- 2020-09: Vue 发布了3.0版本,官方支持 TypeScript
- 2021-11: v4.5版本发布
TypeScript (静态类型、弱类型语言) 源于 JavaScript (动态类型、弱类型语言)。
- 动态类型:执行阶段才去确定类型的匹配(控制台执行)
- 静态类型:类型匹配会提前进行,编译的时候(像 python 、 Java)
- 弱类型语言:类型转换(字符串和数字可相加,强类型就不可以)
TypeScript 特点:
- 静态类型
- 可读性增强:基于 TypeScript 语法解析 TSDoc , ide 增强
- 例如:可以自动生成文档,类型基本上就说明了含义,实现类型相当于在写一个文档
- 可维护性增强:在编译阶段暴露大部分错误 => 多人合作的大型项目中,获得更好的稳定性和开发效率
- 例如:语法拼写错误、类型匹配错误
- 可读性增强:基于 TypeScript 语法解析 TSDoc , ide 增强
- JS 的超集
- 包含于兼容所有 JS 特性,支持共存
- 支持渐进式引入与升级
编辑器推荐:
- Visual Studio Code
- TypeScript 官网的在线编辑器
基本语法
基本数据类型
对象类型
函数类型
函数特性 —— 重载:允许我们有多个具有相同名称但不同参数列表的函数。
规则:
- 两个函数有不同的参数类型
- 两个函数的参数数量不同
- 两个函数有不同的参数类型顺序
简化:函数的编写形式有时候对我们来说是比较麻烦,为了更方便的去改变我们编写代码。
图中简化会报错,报错内容为注释内容。
TypeScript 做了两个步骤:
- 定义了一个匿名函数(
(type, timeStamp) => {...}),将匿名函数赋值给了变量 getDate2 并且约定了 getDate2 类型为 IGetDate 类型; - 匿名函数赋值给了变量 getDate2 ,这时候发生了一次类型匹配,因为我们没有对匿名函数进行函数类型声明, TypeScript 会对它做一次类型推断,然后把匿名函数类型和 IGetDate 类型做匹配。
TypeScript 将匿名函数推断为 (type: "string" | "date", timeStamp: string | undefined) => string | Date , IGetDate 存在 函数(除第三个函数声明)的范围表达小于匿名函数的范围表达,所以报错。
更改方案:
- 将前面两个重载注释掉。
- 将前两个重载的 type 和返回值类型都改成 any 。
个人觉得还是别简化了,更麻烦也更难理解。
数组类型
TypeScript 的补充类型
TypeScript 泛型
泛型:不预先指定具体的类型,而在使用的时候再指定类型,它是一种变量指代。
图中划红线的方式比较粗暴,也达不到想要的效果(比如:传入一个数字时它应该返回的是数字数组),返回完以后的整个传入参数会造成类型的丢失,因为它被重写成了 any 。
T 就是我们所说的泛型,然后把 target 给了一个 T ,返回 T 的一个数组, T 在函数的执行过程中才去确定类型(根据传入内容的类型)。
泛型不仅仅可以在函数中使用:
泛型两个比较高级的语法
- 泛型约束
- 泛型的默认类型
类型别名 & 类型断言
字符串/数字 字面量
允许指定 字符串/数字 必须的固定值:
高级类型
例子
联合/交叉 类型
问题:
- 右边的类型声明的代码比左边多;
- 存在重复元素,例如: author ;
- 元素 type 描述不准确,左边只能取值两种 history 和 story ,右边为 string 。
- 联合类型: IA | IB ;联合类型表示一个值可以是几种类型之一
- 交叉类型: IA & IB ;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
类型保护与类型守卫
类型保护
reverse 方法只有数组才支持,字符串其实是不支持 reverse 的。
typeof 和 instanceof 是 TypeScript 内置的类型保护,当使用 typeof 时, TypeScript 能自动识别出符合它的一个类型。
typeof 和 instanceof 的区别:
typeof 的返回值是一个字符串,用来说明变量的数据类型; instanceof 的返回值是布尔值,用于判断一个变量是否属于某个对象的实例。
类型守卫
: arg is IA 是类型谓词,当函数 getIsIA 的返回值为 true 时,输入的参数 arg 一定是 IA 类型。
这样的函数只能返回布尔值。
!!(arg as IA).a 当它存在 a 时断言它一定是 IA 类型。
当两个类型完全没有任何重合点时我们才要去写一个类型守卫。
高级类型
第一种方式(merge1)比较安全,子集不污染的合并。
第二种方式(merge2)对象结构的方式来进行快速的实现,对象结构的方式有可能造成污染。(不理解)
怎么样为上图函数编写类型
方式一:
方式二:
不知道会传入一个什么样的 Object ,也就是类型定义的时候是不明确的,在使用时才明确。
Record 是高级类型,第一个泛型(string)定义的 Object 中 key 的类型,第二个泛型表示的是 Object 中 value 的类型,所以 Record<string, any> 就是一个 any Object 的简单表达。
Partial 为 TypeScript 内置类型, IPartial 写的是 Partial 具体实现。
函数返回值类型
入参是函数类型,出参是函数的返回值,函数的返回值应该怎么样定义呢?函数的返回值在定义时是不明确的,在使用时才能确认函数的返回值是什么类型的。
<T extends () => any> 中 () => any 表示函数。
在<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any 中,第二个 extends 表示类型推断相当于三元表达式中的“===”。(extends 跟随泛型出现并且出现在类型定义(不是声明)中时,表示类型推断)
ReturnType 为 TypeScript 内置类型, IReturnType 写的是 ReturnType 具体实现。
工程应用
浏览器 Web
- 配置 webpack loader 相关配置;
- 配置 tsconfig.json 文件;
- 允许 webpack 启动/打包;
- loader 处理 ts 文件时会进行编译与类型检查。
webpack loader 把 webpack 中不能识别的文件(ts 文件)转化成 webpack 可识别的文件(js 文件), webpack 主要是处理 js 文件的。
相关 loader :
NodeJs
- 安装 Node 与 npm ;
- 配置 tsconfig.json 文件;
- 使用 npm 安装 tsc ;
- 使用 tsc 运行编译得到 js 文件。