面向新程序员的 TypeScript
祝贺你选择 TypeScript 作为你的第一门语言之一——你已经做出了明智的决定!
你可能已经听说过 TypeScript 是 JavaScript 的一种“风味”或“变体”。 TypeScript (TS) 和 JavaScript (JS) 之间的关系在现代编程语言中是相当独特的,因此更多地了解这种关系将有助于你理解 TypeScript 是如何为 JavaScript 增添价值的。
什么是 JavaScript?简史
JavaScript(也称为 ECMAScript)最初是作为浏览器的一种简单脚本语言而诞生的。 在它被发明的时候,人们期望它用于嵌入在网页中的短小代码片段——编写超过几十行代码的情况是比较少见的。 因此,早期的网络浏览器执行此类代码的速度相当慢。 然而,随着时间的推移,JS 变得越来越流行,Web 开发人员开始使用它来创造交互式体验。
浏览器开发商通过优化其执行引擎(动态编译)和扩展其功能(添加 API)来应对 JS 使用量的增加,这反过来又促使 Web 开发人员更多地使用它。 在现代网站上,你的浏览器经常运行着跨越数十万行代码的应用程序。 这就是“网络”长期而渐进的发展,从一个简单的静态页面网络,演变为一个承载各种丰富应用程序的平台。
不仅如此,JS 已经变得足够流行,以至于可以在浏览器环境之外使用,例如使用 node.js 实现 JS 服务器。 JS“随处运行”的特性使其成为跨平台开发的一个有吸引力的选择。 如今,有许多开发人员仅使用 JavaScript 来编写他们的整个技术栈!
总而言之,我们有一种为快速使用而设计的语言,然后发展成为一个用于编写数百万行应用程序的成熟工具。 每种语言都有其自身的* quirks ——奇特之处和令人惊讶的地方,而 JavaScript humble 的起点使其拥有很多*这样的特性。举几个例子:
JavaScript 的相等操作符(
==)会强制转换其操作数,导致意外行为:jsif ("" == 0) { // 是的!但为什么呢?? } if (1 < x < 3) { // 对于 *任何* x 值都为 true! }JavaScript 还允许访问不存在的属性:
jsconst obj = { width: 10, height: 15 }; // 为什么是 NaN?拼写太难了! const area = obj.width * obj.heigth;
大多数编程语言在发生这类错误时会抛出错误,有些会在编译期间——在任何代码运行之前——这样做。 在编写小型程序时,这些 quirks 虽然烦人但可以管理;而在编写数百或数千行代码的应用程序时,这些不断的意外就成了一个严重的问题。
TypeScript:一个静态类型检查器
我们之前提到,有些语言根本不允许那些有 bug 的程序运行。 在不运行代码的情况下检测代码中的错误被称为静态检查。 根据被操作值的种类来确定什么是错误、什么不是错误,被称为静态类型检查。
TypeScript 在执行之前检查程序是否存在错误,并且基于值的种类进行检查,使其成为一个静态类型检查器。 例如,上面的最后一个例子因为 obj 的类型而出现错误。 这是 TypeScript 发现的错误:
JavaScript 的一个带类型的超集
那么,TypeScript 与 JavaScript 是什么关系呢?
语法
TypeScript 是一种超集语言:因此 JS 语法在 TS 中也是合法的。 语法指的是我们编写文本以形成程序的方式。 例如,这段代码有一个语法错误,因为它缺少一个 ):
let a = (4TypeScript 不会因为语法问题而将任何 JavaScript 代码视为错误。 这意味着你可以将任何能正常工作的 JavaScript 代码放入 TypeScript 文件中,而无需担心它的具体编写方式。
类型
然而,TypeScript 是一个带类型的超集,这意味着它添加了关于如何使用不同类型的值的规则。 之前关于 obj.heigth 的错误不是语法错误:它是以不正确的方式使用某种值(一种类型)的错误。
再举一个例子,这是一段你可以在浏览器中运行的 JavaScript 代码,它会打印一个值:
console.log(4 / []);这段语法上合法的程序打印 Infinity。 然而,TypeScript 认为数字除以数组是一个无意义的操作,并且会报错:
你可能确实打算将一个数字除以一个数组,也许只是想看看会发生什么,但大多数情况下,这是一个编程错误。 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,不应该忽略这些资源。 例如,StackOverflow 上标记为 javascript 的问题数量大约是 typescript 的 20 倍,但所有 javascript 问题也适用于 TypeScript。
如果你发现自己在搜索诸如“如何在 TypeScript 中对列表排序”之类的内容,请记住:TypeScript 是 JavaScript 的运行时加上一个编译时类型检查器。 在 TypeScript 中对列表排序的方式与在 JavaScript 中相同。 如果你找到直接使用 TypeScript 的资源,那也很好,但不要将自己限制在认为需要针对日常运行时任务完成方式的 TypeScript 特定答案上。
下一步
这是对日常 TypeScript 中使用的语法和工具的简要概述。从这里开始,你可以: