|
1 | 1 | # 什么是 TypeScript
|
2 | 2 |
|
3 |
| -首先,我对 TypeScript 的理解如下: |
| 3 | +> Typed JavaScript at Any Scale. |
| 4 | +> 添加了类型系统的 JavaScript,适用于任何规模的项目。 |
4 | 5 |
|
5 |
| -[TypeScript][] 是 JavaScript 的一个超集,主要提供了**类型系统**和**对 ES6 的支持**,它由 Microsoft 开发,代码[开源于 GitHub](https://github.com/Microsoft/TypeScript) 上。 |
| 6 | +以上描述是官网<sup>[[1]](#link-1)</sup>对于 TypeScript 的定义。 |
6 | 7 |
|
7 |
| -其次引用[官网][TypeScript]的定义: |
| 8 | +它强调了 TypeScript 的两个最重要的特性——类型系统、适用于任何规模。 |
8 | 9 |
|
9 |
| -> TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source. |
| 10 | +## TypeScript 的特性 |
10 | 11 |
|
11 |
| -翻译成中文即是: |
| 12 | +### 类型系统 |
12 | 13 |
|
13 |
| -> TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上。TypeScript 是开源的。 |
| 14 | +从 TypeScript 的名字就可以看出来,「类型」是其最核心的特性。 |
14 | 15 |
|
15 |
| -## 为什么选择 TypeScript |
| 16 | +我们知道,JavaScript 是一门非常灵活的编程语言: |
16 | 17 |
|
17 |
| -[TypeScript 官网][TypeScript]列举了一些优势,不过我更愿意自己总结一下: |
| 18 | +- 它没有类型约束,一个变量可能初始化时是字符串,过一会儿又被赋值为数字。 |
| 19 | +- 由于隐式类型转换的存在,有的变量的类型很难在运行前就确定。 |
| 20 | +- 基于原型的面向对象编程,使得原型上的属性或方法可以在运行时被修改。 |
| 21 | +- 函数是 JavaScript 中的一等公民<sup>[[2]](#link-2)</sup>,可以赋值给变量,也可以当作参数或返回值。 |
18 | 22 |
|
19 |
| -### TypeScript 增加了代码的可读性和可维护性 |
| 23 | +这种灵活性就像一把双刃剑,一方面使得 JavaScript 蓬勃发展,无所不能,从 2013 年开始就一直蝉联最普遍使用的编程语言排行榜冠军<sup>[[3]](#link-3)</sup>;另一方面也使得它的代码质量参次不起,维护成本高,运行时错误多。 |
20 | 24 |
|
21 |
| -- 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了 |
22 |
| -- 可以在编译阶段就发现大部分错误,这总比在运行时候出错好 |
23 |
| -- 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、代码重构等 |
| 25 | +而 TypeScript 的类型系统,在很大程度上弥补了 JavaScript 的缺点。 |
24 | 26 |
|
25 |
| -### TypeScript 非常包容 |
| 27 | +#### TypeScript 是静态类型 |
26 | 28 |
|
27 |
| -- TypeScript 是 JavaScript 的超集,`.js` 文件可以直接重命名为 `.ts` 即可 |
28 |
| -- 即使不显式的定义类型,也能够自动做出[类型推论](../basics/type-inference.md) |
29 |
| -- TypeScript 的类型系统是图灵完备的,可以定义从简单到复杂的几乎一切类型 |
30 |
| -- 即使 TypeScript 编译报错,也可以生成 JavaScript 文件 |
31 |
| -- 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取 |
| 29 | +类型系统按照「类型检查的时机」来分类,可以分为动态类型和静态类型。 |
32 | 30 |
|
33 |
| -### TypeScript 拥有活跃的社区 |
| 31 | +动态类型是指在运行时才会进行类型检查,这种语言的类型错误往往会导致运行时错误。JavaScript 是一门解释型语言<sup>[[4]](#link-4)</sup>,没有编译阶段,所以它是动态类型,以下这段代码在运行时才会报错: |
34 | 32 |
|
35 |
| -- 大部分第三方库都有提供给 TypeScript 的类型定义文件 |
36 |
| -- Angular、Vue、VS Code、Ant Design 等等耳熟能详的项目都是使用 TypeScript 编写的 |
37 |
| -- TypeScript 拥抱了 ES6 规范,支持 ESNext 草案中处于第三阶状态(Stage 3)的特性 |
| 33 | +```js |
| 34 | +let foo = 1; |
| 35 | +foo.split(' '); |
| 36 | +// Uncaught TypeError: foo.split is not a function |
| 37 | +// 运行时会报错(foo.split 不是一个函数),造成线上 bug |
| 38 | +``` |
38 | 39 |
|
39 |
| -### TypeScript 的缺点 |
| 40 | +静态类型是指编译阶段就能确定每个变量的类型,这种语言的类型错误往往会导致语法错误。TypeScript 在运行前需要先编译为 JavaScript,而在编译阶段就会进行类型检查,所以 **TypeScript 是静态类型**,这段 TypeScript 代码在编译阶段就会报错了: |
40 | 41 |
|
41 |
| -任何事物都是有两面性的,我认为 TypeScript 的弊端在于: |
| 42 | +```ts |
| 43 | +let foo = 1; |
| 44 | +foo.split(' '); |
| 45 | +// Property 'split' does not exist on type 'number'. |
| 46 | +// 编译时会报错(数字没有 split 方法),无法通过编译 |
| 47 | +``` |
42 | 48 |
|
43 |
| -- 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念 |
44 |
| -- 短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本 |
45 |
| -- 集成到构建流程需要一些工作量 |
46 |
| -- 可能和一些库结合的不是很完美 |
| 49 | +你可能会奇怪,这段 TypeScript 代码看上去和 JavaScript 没有什么区别呀。 |
47 | 50 |
|
48 |
| -大家可以根据自己团队和项目的情况判断是否需要使用 TypeScript。 |
| 51 | +没错!大部分 JavaScript 代码都只需要经过少量的修改(或者完全不用修改)就变成 TypeScript 代码,这得益于 TypeScript 强大的[类型推论][],即使不去手动声明变量 `foo` 的类型,也能在变量初始化时自动推论出它是一个 `number` 类型。 |
49 | 52 |
|
50 |
| -[TypeScript]: http://www.typescriptlang.org/ |
| 53 | +完整的 TypeScript 代码是这样的: |
| 54 | + |
| 55 | +```ts |
| 56 | +let foo: number = 1; |
| 57 | +foo.split(' '); |
| 58 | +// Property 'split' does not exist on type 'number'. |
| 59 | +// 编译时会报错(数字没有 split 方法),无法通过编译 |
| 60 | +``` |
| 61 | + |
| 62 | +#### TypeScript 是弱类型 |
| 63 | + |
| 64 | +类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型。 |
| 65 | + |
| 66 | +以下这段代码不管是在 JavaScript 中还是在 TypeScript 中都是可以正常运行的,运行时数字 `1` 会被隐式类型转换为字符串 `'1'`,加号 `+` 被识别为字符串拼接,所以打印出结果是字符串 `'11'`。 |
| 67 | + |
| 68 | +```js |
| 69 | +console.log(1 + '1'); |
| 70 | +// 打印出字符串 '11' |
| 71 | +``` |
| 72 | + |
| 73 | +TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性,所以**它们都是弱类型**。 |
| 74 | + |
| 75 | +作为对比,Python 是强类型,以下代码会在运行时报错: |
| 76 | + |
| 77 | +```py |
| 78 | +print(1 + '1') |
| 79 | +# TypeError: unsupported operand type(s) for +: 'int' and 'str' |
| 80 | +``` |
| 81 | + |
| 82 | +若要修复该错误,需要进行强制类型转换: |
| 83 | + |
| 84 | +```py |
| 85 | +print(str(1) + '1') |
| 86 | +# 打印出字符串 '11' |
| 87 | +``` |
| 88 | + |
| 89 | +> 强/弱是相对的,Python 在处理整型和浮点型相加时,会将整型隐式转换为浮点型,但是这并不影响 Python 是强类型的结论,因为大部分情况下 Python 并不会进行隐式类型转换。相比而言,JavaScript 和 TypeScript 中不管加号两侧是什么类型,都可以通过隐式类型转换计算出一个结果——而不是报错——所以 JavaScript 和 TypeScript 都是弱类型。 |
| 90 | +
|
| 91 | +> 虽然 TypeScript 不限制加号两侧的类型,但是我们可以借助 TypeScript 提供的类型系统,以及 ESLint 提供的代码检查功能,来限制加号两侧必须同为数字或同为字符串<sup>[[5]](#link-5)</sup>。这在一定程度上使得 TypeScript 向「强类型」更近一步了——当然,这种限制是可选的。 |
| 92 | +
|
| 93 | +这样的类型系统体现了 TypeScript 的核心设计理念<sup>[[6]](#link-6)</sup>:在完整保留 JavaScript 运行时行为的基础上,通过引入静态类型系统来提高代码的可维护性,减少可能出现的 bug。 |
| 94 | + |
| 95 | +### 适用于任何规模 |
| 96 | + |
| 97 | +TypeScript 非常适用于大型项目——这是显而易见的,类型系统可以为大型项目带来更高的可维护性,以及更少的 bug。 |
| 98 | + |
| 99 | +在中小型项目中推行 TypeScript 的最大障碍就是认为使用 TypeScript 需要写额外的代码,降低开发效率。但事实上,由于有[类型推论][],大部分类型都不需要手动声明了。相反,TypeScript 增强了编辑器(IDE)的功能,包括代码补全、接口提示、跳转到定义、代码重构等,这在很大程度上提高了开发效率。而且 TypeScript 有近百个[编译选项][],如果你认为类型检查过于严格,那么可以通过修改编译选项来降低类型检查的标准。 |
| 100 | + |
| 101 | +TypeScript 还可以和 JavaScript 共存。这意味着如果你有一个使用 JavaScript 开发的旧项目,又想使用 TypeScript 的特性,那么你不需要急着把整个项目都迁移到 TypeScript,你可以使用 TypeScript 编写新文件,然后在后续更迭中逐步迁移旧文件。如果一些 JavaScript 文件的迁移成本太高,TypeScript 也提供了一个方案,可以让你在不修改 JavaScript 文件的前提下,编写一个[类型声明文件][],实现旧项目的渐进式迁移。 |
| 102 | + |
| 103 | +事实上,就算你从来没学习过 TypeScript,你也可能已经在不知不觉中使用到了 TypeScript——在 VSCode 编辑器中编写 JavaScript 时,代码补全和接口提示等功能就是通过 TypeScript Language Service 实现的<sup>[[7]](#link-7)</sup>: |
| 104 | + |
| 105 | + |
| 106 | + |
| 107 | +一些第三方库原生支持了 TypeScript,在使用时就能获得代码补全了,比如 Vue 3.0<sup>[[8]](#link-8)</sup>: |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | +有一些第三方库原生不支持 TypeScript,但是可以通过安装社区维护的类型声明库<sup>[[9]](#link-9)</sup>(比如通过运行 `npm install --save-dev @types/react` 来安装 React 的类型声明库)来获得代码补全能力——不管是在 JavaScript 项目中还是在 TypeScript 中项目中都是支持的: |
| 112 | + |
| 113 | + |
| 114 | + |
| 115 | +由此可见,TypeScript 的发展已经深入到前端社区的方方面面了,任何规模的项目都或多或少得到了 TypeScript 的支持。 |
| 116 | + |
| 117 | +### 与标准同步发展 |
| 118 | + |
| 119 | +TypeScript 的另一个重要的特性就是坚持与 ECMAScript 标准<sup>[[10]](#link-10)</sup>同步发展。 |
| 120 | + |
| 121 | +ECMAScript 是 JavaScript 核心语法的标准,自 2015 年起,每年都会发布一个新版本,包含一些新的语法。 |
| 122 | + |
| 123 | +一个新的语法从提案到变成正式标准,需要经历以下几个阶段: |
| 124 | + |
| 125 | +- Stage 0:展示阶段,仅仅是提出了讨论、想法,尚未正式提案。 |
| 126 | +- Stage 1:征求意见阶段,提供抽象的 API 描述,讨论可行性,关键算法等。 |
| 127 | +- Stage 2:草案阶段,使用正式的规范语言精确描述其语法和语义。 |
| 128 | +- Stage 3:候选人阶段,语法的设计工作已完成,需要浏览器、Node.js 等环境支持,搜集用户的反馈。 |
| 129 | +- Stage 4:定案阶段,已准备好将其添加到正式的 ECMAScript 标准中。 |
| 130 | + |
| 131 | +一个语法进入到 Stage 3 阶段后,TypeScript 就会实现它。一方面,让我们可以尽早的使用到最新的语法,帮助它进入到下一个阶段;另一方面,处于 Stage 3 阶段的语法已经比较稳定了,基本不会有语法的变更,这使得我们能够放心的使用它。 |
| 132 | + |
| 133 | +除了实现 ECMAScript 标准之外,TypeScript 团队也推进了诸多语法提案,比如可选链操作符(`?.`)<sup>[[11]](#link-11)</sup>、空值合并操作符(`??`)<sup>[[12]](#link-12)</sup>、Throw 表达式<sup>[[13]](#link-13)</sup>、正则匹配索引<sup>[[14]](#link-14)</sup>等。 |
| 134 | + |
| 135 | +## 总结 |
| 136 | + |
| 137 | +什么是 TypeScript? |
| 138 | + |
| 139 | +- TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目。 |
| 140 | +- TypeScript 是一门静态类型、弱类型的语言。 |
| 141 | +- TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性。 |
| 142 | +- TypeScript 可以编译为 JavaScript,然后运行在浏览器、Node.js 等任何能运行 JavaScript 的环境中。 |
| 143 | +- TypeScript 拥有很多编译选项,类型检查的严格程度由你决定。 |
| 144 | +- TypeScript 可以和 JavaScript 共存,这意味着 JavaScript 项目能够渐进式的迁移到 TypeScript。 |
| 145 | +- TypeScript 增强了编辑器(IDE)的功能,提供了代码补全、接口提示、跳转到定义、代码重构等能力。 |
| 146 | +- TypeScript 拥有活跃的社区,大多数常用的第三方库都提供了类型声明。 |
| 147 | +- TypeScript 与标准同步发展,符合最新的 ECMAScript 标准(stage 3)。 |
| 148 | + |
| 149 | +## 附:TypeScript 的发展历史 |
| 150 | + |
| 151 | +- 2012-10:微软发布了 TypeScript 第一个版本(0.8),此前已经在微软内部开发了两年。 |
| 152 | +- 2014-04:TypeScript 发布了 1.0 版本。 |
| 153 | +- 2014-10:Angular 发布了 2.0 版本,它是一个基于 TypeScript 开发的前端框架。 |
| 154 | +- 2015-01:ts-loader 发布,webpack 可以编译 TypeScript 文件了。 |
| 155 | +- 2015-04:微软发布了 Visual Studio Code,它内置了对 TypeScript 语言的支持,它自身也是用 TypeScript 开发的。 |
| 156 | +- 2016-05:`@types/react` 发布,TypeScript 可以开发 React 应用了。 |
| 157 | +- 2016-05:`@types/node` 发布,TypeScript 可以开发 Node.js 应用了。 |
| 158 | +- 2016-09:TypeScript 发布了 2.0 版本。 |
| 159 | +- 2018-06:TypeScript 发布了 3.0 版本。 |
| 160 | +- 2019-02:TypeScript 宣布由官方团队来维护 typescript-eslint,以支持在 TypeScript 文件中运行 ESLint 检查。 |
| 161 | +- 2020-05:Deno 发布了 1.0 版本,它是一个 JavaScript 和 TypeScript 运行时。 |
| 162 | +- 2020-08:TypeScript 发布了 4.0 版本。 |
| 163 | +- 2020-09:Vue 发布了 3.0 版本,官方支持 TypeScript。 |
| 164 | + |
| 165 | +## 参考资料 |
| 166 | + |
| 167 | +1. <span id="link-1">[TypeScript 官网](https://www.typescriptlang.org/)</span> |
| 168 | +2. <span id="link-2">[第 2 章: 一等公民的函数](https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/content/ch2.html) · 函数式编程指北</span> |
| 169 | +3. <span id="link-3">[StackOverflow 2020 开发者调查报告](https://insights.stackoverflow.com/survey/2020)</span> |
| 170 | +4. <span id="link-4">[斯坦福 JavaScript 第一课](https://web.stanford.edu/class/cs98si/slides/overview.html)</span> |
| 171 | +5. <span id="link-5">[TypeScript ESLint 规则 `restrict-plus-operands`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/restrict-plus-operands.md)</span> |
| 172 | +6. <span id="link-6">[TypeScript 设计理念](https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals)</span> |
| 173 | +7. <span id="link-7">[Visual Studio Code 中集成了 TypeScript](https://code.visualstudio.com/docs/languages/typescript)</span> |
| 174 | +8. <span id="link-8">[Vue 3.0 支持 TypeScript](https://v3.vuejs.org/guide/typescript-support.html)</span> |
| 175 | +9. <span id="link-9">[Definitely Typed](https://github.com/DefinitelyTyped/DefinitelyTyped)——TypeScript 团队帮助维护的类型定义仓库</span> |
| 176 | +10. <span id="link-10">[ECMAScript 标准](https://tc39.es/process-document/)</span> |
| 177 | +11. <span id="link-11">[可选链操作符(`?.`)](https://github.com/tc39/proposal-optional-chaining)</span> |
| 178 | +12. <span id="link-12">[空值合并操作符(`??`)](https://github.com/tc39/proposal-nullish-coalescing)</span> |
| 179 | +13. <span id="link-13">[Throw 表达式](https://github.com/tc39/proposal-throw-expressions)</span> |
| 180 | +14. <span id="link-14">[正则匹配索引](https://github.com/tc39/proposal-regexp-match-indices)</span> |
0 commit comments