恭喜你把 TypeScript 选为入门语言之一——你已经做出了很好的决定!
你可能已经听说过,TypeScript 是 JavaScript 的一种“风味”或“变体”。在现代编程语言中,TypeScript(TS)与 JavaScript(JS)之间的关系相当独特,因此进一步了解这种关系将帮助你理解 TypeScript 如何为 JavaScript 增色。
什么是 JavaScript?一段简史
JavaScript(又称 ECMAScript)最初只是用于浏览器的一种简单脚本语言。在它被发明时,人们预期它只用于嵌入网页中的短小代码片段——编写超过几十行的代码在当时颇不寻常。因此,早期的 Web 浏览器执行这类代码相当缓慢。但随着时间推移,JS 越来越流行,Web 开发者开始用它来打造交互式体验。
Web 浏览器开发者对 JS 使用量的增长作出回应:优化执行引擎(动态编译),并扩展它能做之事(添加 API),这反过来又促使 Web 开发者更多地使用它。在现代网站上,你的浏览器经常运行着跨越数十万行代码的应用程序。这就是“Web”漫长而渐进的发展历程:从简单的静态页面网络,演变为承载各类丰富应用的平台。
不仅如此,JS 的流行程度已经足以在浏览器之外使用,例如用 node.js 实现 JS 服务器。JS“随处运行”的特性使其成为跨平台开发的一个有吸引力的选择。如今有很多开发者只用 JavaScript 来编写他们的整个技术栈!
总而言之,我们有一种最初为快速使用而设计的语言,后来却成长为可用来编写数百万行应用程序的成熟工具。每种语言都有自己的怪癖——古怪之处和意外之举,而 JavaScript 卑微的出身使它拥有很多这样的怪癖。一些例子:
-
JavaScript 的相等运算符(
==)会对其操作数进行强制转换,从而导致意外行为:jsif ("" == 0) {// It is! But why??}if (1 < x < 3) {// True for *any* value of x!} -
JavaScript 还允许访问并不存在的属性:
jsconst obj = { width: 10, height: 15 };// Why is this NaN? Spelling is hard!const area = obj.width * obj.heigth;
大多数编程语言在发生这类错误时会抛出错误,有些语言会在编译期间抛出——即在任何代码运行之前。编写小程序时,这类怪癖虽然烦人但尚可应付;而编写数百或数千行的应用程序时,这些层出不穷的意外就成了严重问题。
TypeScript:静态类型检查器
我们前面说过,有些语言根本不允许那些有 bug 的程序运行。在不运行代码的情况下检测错误称为静态检查。根据所操作值的种类来判定什么算错误、什么不算,称为静态类型检查。
TypeScript 在执行前检查程序中的错误,并且是依据值的种类来检查的,因此它是一个静态类型检查器。例如,上面的最后一个示例之所以有错,是因为 obj 的类型。下面是 TypeScript 发现的错误:
tsTryconstobj = {width : 10,height : 15 };constProperty 'heigth' does not exist on type '{ width: number; height: number; }'. Did you mean 'height'?2551Property 'heigth' does not exist on type '{ width: number; height: number; }'. Did you mean 'height'?area =obj .width *obj .; heigth
JavaScript 的类型化超集
那么,TypeScript 与 JavaScript 是什么关系呢?
语法
TypeScript 是 JavaScript 的超集:因此 JS 语法就是合法的 TS。语法指的是我们编写文本以构成程序的方式。例如,下面这段代码有一个语法错误,因为它缺少一个 ):
tsTrylet')' expected.1005')' expected.a = (4
TypeScript 不会因为语法问题而把任何 JavaScript 代码视为错误。这意味着你可以把任何能正常工作的 JavaScript 代码放进 TypeScript 文件,而无需担心它的具体写法。
类型
然而,TypeScript 是类型化的超集,这意味着它增加了关于如何使用不同种类值的规则。前面关于 obj.heigth 的错误并不是语法错误:它是以不正确的方式使用某种值(类型)的错误。
再举一个例子,下面这段 JavaScript 代码可以在浏览器中运行,并且它会打印出一个值:
jsconsole.log(4 / []);
这个语法上合法的程序会打印 Infinity。但 TypeScript 认为用数字除以数组是无意义的操作,因此会报错:
tsTryThe right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.2363The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.console .log (4 /[] );
你确实有可能就是想用数字除以数组,也许只是想看看会发生什么,但大多数情况下这是编程错误。TypeScript 的类型检查器旨在放行正确的程序,同时尽可能多地捕获常见错误。(稍后我们会了解可用于配置 TypeScript 检查代码严格程度的设置。)
如果你把一些代码从 JavaScript 文件移到 TypeScript 文件,可能会看到类型错误,这取决于代码的写法。这些错误可能是代码确实有问题,也可能是 TypeScript 过于保守。在本指南中,我们将演示如何添加各种 TypeScript 语法来消除这类错误。
运行时行为
TypeScript 还是一种保留 JavaScript 运行时行为的编程语言。例如,在 JavaScript 中除以零会得到 Infinity,而不是抛出运行时异常。作为一项原则,TypeScript 从不改变 JavaScript 代码的运行时行为。
这意味着,如果你把代码从 JavaScript 移到 TypeScript,它保证会以相同方式运行,即使 TypeScript 认为代码存在类型错误。
与 JavaScript 保持相同的运行时行为是 TypeScript 的一项根本承诺,因为这意味着你可以在两种语言之间轻松迁移,而不必担心某些细微差异会让程序停止工作。
类型擦除
大致来说,TypeScript 编译器完成代码检查后,会擦除类型,从而生成最终的“编译后”代码。这意味着代码编译后,得到的普通 JS 代码不包含任何类型信息。
这也意味着 TypeScript 从不根据它推断出的类型来改变程序的行为。归根结底,虽然你在编译期间可能看到类型错误,但类型系统本身并不影响程序运行时的行为。
最后,TypeScript 不提供任何额外的运行时库。你的程序将使用与 JavaScript 程序相同的标准库(或外部库),因此没有额外的 TypeScript 专属框架需要学习。
学习 JavaScript 与 TypeScript
我们经常看到这样的问题:“我应该学 JavaScript 还是 TypeScript?”
答案是:不学 JavaScript 就无法学会 TypeScript!TypeScript 与 JavaScript 共享语法和运行时行为,因此你学到的任何 JavaScript 知识都在同时帮助你学习 TypeScript。
可供程序员学习 JavaScript 的资源非常非常多;如果你在写 TypeScript,你不应忽视这些资源。例如,标记为 javascript 的 StackOverflow 问题数量约为标记为 typescript 的 20 倍,但所有 javascript 问题也同样适用于 TypeScript。
如果你发现自己在搜索类似“如何在 TypeScript 中对列表排序”的问题,请记住:TypeScript 就是带有编译时类型检查器的 JavaScript 运行时。在 TypeScript 中对列表排序的方式与在 JavaScript 中完全相同。如果你找到的资料直接使用了 TypeScript,那也很好,但不要让自己局限于认为日常关于如何完成运行时任务的问题都需要 TypeScript 专属的答案。
后续步骤
以上简要介绍了日常 TypeScript 开发中使用的语法和工具。从这里出发,你可以:
-
学习一些 JavaScript 基础知识,我们推荐以下任一资源:
-
阅读完整的手册,从头到尾