第 1 章 从 JavaScript 到 TypeScript

第 1 章 从 JavaScript 到 TypeScript

Flying
2022-09-26 / 0 评论 / 109 阅读 / 正在检测是否收录...

今天的
JavaScript 支持几十年前的浏览器
网络之美

在谈论 TypeScript 之前,我们需要先了解它的来源:JavaScript!

JavaScript 的历史

1995年,Netscape 公司的 Brendan Eich 花了10天时间设计出了易用的JavaScript。从那以后,开发人员一直在取笑它的怪癖和明显的缺点。我将在下一节中介绍其中的一些。

然而,自1995年以来,JavaScript已经有了巨大的发展!它的指导委员会 TC39 发布了 ECMAScript 的新版本, ECMAScript 是JavaScript自 2015 年以来每年基于的语言规范,其中包含了使 JavaScript 与其他现代语言保持一致的新特性。令人印象深刻的是,即使是常规的新语言版本,JavaScript 在不同的环境(包括浏览器、嵌入式应用程序和服务器运行时)中仍然能够保持向后兼容性数十年。

今天,JavaScript 是一门非常灵活的语言,具有很多优点。我们应该意识到,虽然 JavaScript 有它的怪癖,但它也帮助实现了 Web 应用程序和互联网的惊人增长。

给我看完美的编程语言,我会给你展示一种没有用户的语言。
——安德斯·海尔斯伯格,TSConf 2019

原生 JavaScript 的缺点

开发人员经常将没有任何重要语言扩展或框架的 JavaScript 称为“原生”:指的是它是熟悉的原始风格。我很快就会讨论为什么 TypeScript 添加了恰到好处的味道来克服这些特定的主要缺点,但是理解为什么它们会很痛苦是很有用的。所有这些弱点都变得更加明显,项目越大,寿命越长。

昂贵的自由

不幸的是,许多开发人员对 JavaScript 最大的抱怨是它的一个关键特性:JavaScript 几乎没有对如何构建代码进行任何限制。这种自由让用 JavaScript 启动项目变得非常有趣!

然而,当你拥有越来越多的文件时,这种自由的破坏性就变得很明显了。从一些虚构的绘画应用程序中提取以下片段:

function paintPainting(painter, painting) {
  return painter
    .prepare()
    .paint(painting, painter.ownMaterials)
    .finish();
}

在没有任何上下文的情况下阅读该代码,你只能对如何调用 paintPainting 函数有模糊的想法。如果你使用过相关的代码库,你可能会记得 painter 应该是某个 getPainter 函数返回的内容。你甚至可以幸运地猜测 painting 是一个字符串。

然而,即使这些假设是正确的,以后对代码的更改也可能使它们失效。也许 painting 从字符串更改为其他数据类型,或者 painter 的一个或多个方法被重命名。

如果其他语言的编译器确定代码可能会崩溃,它们可能会拒绝让你运行代码。动态类型语言(那些运行代码而不检查代码是否可能首先崩溃的语言)则不是这样,比如 JavaScript。

当你想要安全运行代码时,让 JavaScript 变得如此有趣的代码自由变成了真正的痛苦。

松散的文档

JavaScript 语言规范中没有任何内容来形式化描述代码中的函数参数、函数返回值、变量或其他构造的含义。许多开发人员采用了一种称为 JSDoc 的标准,使用块注释来描述函数和变量。JSDoc 标准描述了你可能如何编写直接放置在函数和变量之上的文档注释,并以标准的格式进行格式化。以下是一个例子,这个例子是从上下文中提取出来的:

/**
* Performs a painter painting a particular painting.
*
* @param {Painting} painter
* @param {string} painting
* @returns {boolean} Whether the painter painted the painting.
*/
function paintPainting(painter, painting) { /* ... */ }

JSDoc 有一些关键问题,通常会让它在大型代码库中使用不愉快:

  • 没有什么能阻止 JSDoc 对代码的描述是错误的。
  • 即使你的 JSDoc 描述以前是正确的,在代码重构期间,也很难找到与你的更改相关的所有现在无效的 JSDoc 注释。
  • 描述复杂对象既笨拙又冗长,需要多个独立的注释来定义类型及其关系。

在十几个文件中维护 JSDoc 注释并不会占用太多时间,但是在数百甚至数千个不断更新的文件中维护 JSDoc注释确实是一件苦差事

较弱的开发工具

由于 JavaScript 不提供识别类型的内置方法,并且代码很容易与 JSDoc 注释不同,因此很难自动对代码库进行大量更改或获得有关代码库的见解。JavaScript 开发人员经常惊讶地看到 C# 和 Java等类型化语言中的特性,允许开发人员执行类成员重命名或跳转到声明参数类型的位置。

你可能会抗议现代 IDE(如 VS Code)确实提供了一些开发工具,例如对 JavaScript 的自动重构。没错,但是:它们在后台使用 TypeScript 或类似的许多 JavaScript 功能,并且这些开发工具在大多数 JavaScript 代码中不像在定义良好的 TypeScript 代码中那样可靠或强大。

TypeScript!

TypeScript 于 2010 年代初在微软内部创建,然后在 2012 年发布并开源。其开发的负责人是Anders Hejlsberg,他也以领导流行的 C# 和 Turbo Pascal 语言的开发而闻名。TypeScript 通常被描述为“JavaScript 的超集”或“带有类型的 JavaScript”。但什么 TypeScript呢?

TypeScript 有四个方面:

  • 编程语言
    一种语言,包括所有现有的 JavaScript 语法,以及用于定义和使用类型的新 TypeScript 特定语法
  • 类型检查器
    一个程序,它接受一组用 JavaScript 和/或TypeScript 编写的文件,了解创建的所有结构(变量,函数……),并让你知道它是否认为任何设置不正确。
  • 编译器
    一个类型检查器程序,报告任何问题,然后输出等效 JavaScript 代码
  • 语言服务
    一个程序,使用类型检查器告诉编辑器(如 VS Code)如何为开发人员提供有用的实用程序

从 TypeScript 演练场开始

到目前为止,你已经阅读了大量有关 TypeScript 的信息。让我们开始写吧!

主要的 TypeScript 网站包括一个“演练场”编辑器,网址为。https://www.typescriptlang.org/play。你可以在主编辑器中键入代码,并看到许多与在完整的 IDE(集成开发环境)本地使用 TypeScript 时相同的编辑器建议。

本书中的大多数片段都特意很小且足够独立,你可以在演练场中键入它们,并对其进行修改以获得乐趣。

TypeScript 实战

看看这个代码片段:

const firstName = "Georgia";
const nameLength = firstName.length();
// ~~~~~~
// 此表达式不可调用。

这段代码使用的是普通的 JavaScript 语法,我还没有介绍 TypeScript 特定的语法。如果你在这段代码上运行 TypeScript 类型检查器,它会利用它对字符串的 length 属性是一个数字而不是一个函数的知识,给出上面注释中显示的错误提示。

如果你要把这段代码粘贴到演练场或编辑器中,语言服务会告诉你在 length 下面有一些红色波浪线,表示 TypeScript 对你的代码不满意。将鼠标悬停在波浪线代码上,你会看到错误信息(图 1-1)。

figure-1-1.jpg
图 1-1 TypeScript 报告字符串长度不可调用的错误

当你输入这些错误时,在编辑器中被告知这些简单的错误,这比等特定的代码行运行并抛出错误要愉快得多。如果你试图用 JavaScript 运行该代码,它会崩溃!

通过限制获得自由

TypeScript 允许我们指定可以为参数和变量提供哪些类型的值。一些开发人员发现,首先必须在代码中明确写出特定区域应该如何工作是有限制性的。

我认为以这种方式被“限制”实际上是一件好事!通过将我们的代码限制为只能以你指定的方式使用,TypeScript 可以让你确信一个代码区域中的更改不会破坏使用它的其他代码区域。

例如,如果你更改了函数所需的参数数量,TypeScript 会在你忘记更新调用该函数的位置时通知你。

在下面的示例中,sayMyName 已从接受两个参数更改为接受一个参数,但使用两个字符串对它的调用未更新,因此 TypeScript 认为那是错误的:

// 先前: sayMyName(firstName, lastName) { ...
function sayMyName(fullName) {
 console.log(`You acting kind of shady, ain't callin' me ${fullName}`);
}
sayMyName("Beyoncé", "Knowles");
// ~~~~~~~~~
// 应有 1 个参数,但获得 2 个。

该代码将在 JavaScript 中运行而不会崩溃,但它的输出将与预期不同(它不包括“Knowles”):

You acting kind of shady, ain't callin' me Beyoncé

使用错误数量的参数调用函数正是 TypeScript 限制的那种短视的 JavaScript 自由。

精确的文档记录

让我们来看一下之前提到的 paintPainting 函数的 TypeScript 版本。尽管我还没有深入探讨 TypeScript 语法中用于文档类型的特定细节,但以下片段仍然暗示了 TypeScript 可以以多么准确的方式文档化代码:

interface Painter {
  finish(): boolean;
  ownMaterials: Material[];
  paint(painting: string, materials: Material[]): boolean;
}
function paintPainting(painter: Painter, painting: string): boolean { /* ... */ }

一个初次阅读这段代码的 TypeScript 开发人员可以理解 painter 至少有三个属性,其中两个是方法。通过将描述对象“形状”的语法编入代码中,TypeScript 提供了一个出色的、强制执行的系统来描述对象的外观。

更强大的开发工具

TypeScript 的类型允许 VS Code 等编辑器更深入地了解你的代码。然后,他们可以使用这些见解在你键入时显示智能建议。这些建议对开发非常有用。

如果你以前用 VS Code 来编写 JavaScript,你可能已经注意到它会在你使用内置类型的对象(比如字符串)编写代码时建议“自动完成”。如果你开始输入某个已知的字符串成员,TypeScript 可以提示所有字符串成员(图 1-2)。

figure-1-2.jpg
图 1-2 TypeScript 在 JavaScript 中为字符串提供自动完成建议

当你添加 TypeScript 的类型检查器来理解代码时,它甚至可以为你提供这些有用的建议,即使是你编写的代码。在 paintPainting 函数中键入 Painter ,TypeScript 将知道 painter 参数的类型为 Painter,而 Painter 类型具有以下成员(图 1-3)。

figure-1-3.jpg
图 1-3 TypeScript 在 JavaScript 中为字符串提供自动完成建议

牛!我将在第 12 章 “使用 IDE 功能”中介绍大量其他有用的编辑器功能。

编译语法

TypeScript 的编译器允许我们输入 TypeScript 语法,对其进行类型检查,并生成相应的 JavaScript。为了方便起见,编译器还可以采用现代 JavaScript 语法,并将其编译为旧版的 ECMAScript 版本。

如果要将此 TypeScript 代码粘贴到演练场中:

const artist = "Augusta Savage";
console.log({ artist });

演练场会在屏幕的右侧显示编译器生成的相应 JavaScript 代码(图 1-4)。

figure-1-4.jpg
图 1-4 TypeScript 演练场将 TypeScript 代码编译为等效的 JavaScript

TypeScript 演练场是一个很好的工具,用于显示源 TypeScript 如何成为输出 JavaScript。

许多 JavaScript 项目使用专用的转译器,例如 Babel(https://babeljs.io)而不是 TypeScript 自己的将源代码转换为可运行的 JavaScript。你可以在以下位置找到常见项目启动器的列表 https://learningtypescript.com/starters.

从本地开始

只要安装了 Node.js,你就可以在计算机上运行 TypeScript。若要全局安装最新版本的 TypeScript,请运行以下命令:

npm i -g typescript

现在,你将能够使用 tsc(TypeScript Compiler)命令在命令行上运行 TypeScript。尝试使用 --version 标志,以确保它设置正确:

tsc --version

它应该打印出类似 Version X.Y.Z 的内容 — 无论你在安装 TypeScript 时处于哪个最新版本:

tsc --version Version 4.7.2

本地运行

现在 TypeScript 已安装,让我们在本地设置一个文件夹以在代码上运行 Type-Script。在计算机上的某个位置创建一个文件夹,然后运行以下命令以创建新的 tsconfig.json 配置文件:

tsc --init

tsconfig.json 文件声明 TypeScript 在分析代码时使用的设置。在本书中,该文件中的大多数选项都与你无关(在编程中有很多不常见的边缘情况,语言需要考虑!)。我将在第 13 章 “配置选项”中介绍它们。重要的功能是,现在你可以运行 tsc 来告诉 TypeScript 编译该文件夹中的所有文件,TypeScript 将引用该 tsconfig.json 以获取任何配置选项。

尝试添加一个名为 index.ts 的文件,其中包含以下内容:

console.blub("Nothing is worth more than laughter.");

然后,运行 tsc 并为其提供该 index.ts 文件的名称:

tsc index.ts

你应该会得到一个类似如下的错误:

index.ts:1:9 - error TS2339: 类型“Console”上不存在属性“blub”。

1 console.blub("Nothing is worth more than laughter.");
~~~~

发现 1 个错误。

的确,console 上不存在 blub。我当时在想什么呢?

在修复代码以满足 TypeScript 之前,请注意 tsc 为你创建了一个 index.js 其中包含 console.blub 的内容。

这是一个重要的概念:即使我们的代码中存在类型错误,语法仍然完全有效。无论任何类型错误,TypeScript 编译器仍将从输入文件生成 JavaScript。

更正 index.ts 中的代码以调用 console.log 并再次运行 tsc。你的终端中应该没有错误,index.js 文件现在应该包含更新的输出代码:

console.log("Nothing is worth more than laughter.");
我强烈建议你在阅读在阅读的时候玩一下书中的代码片段,无论是在演练场中还是在支持 TypeScript 的编辑器中,这意味着它会为你运行 TypeScript 语言服务。小型独立练习以及大型项目也可用于帮助你练习所学知识 https://learningtypescript.com

编辑器功能

创建 tsconfig.json 文件的另一个好处是,当编辑器打开到特定文件夹时,他们现在可以将该文件夹识别为 TypeScript 项目。例如,如果在文件夹中打开 VS Code,则它用于分析 TypeScript 代码的设置将遵循该文件夹的 tsconfig.json 中的任何内容。

作为练习,请返回本章中的代码片段,并在编辑器中键入它们。你应该会看到下拉列表,建议你在键入名称时完成名称,尤其是对于成员,例如 console 上的 log

非常令人兴奋:你正在使用 TypeScript 语言服务来帮助自己编写代码!你正在成为 TypeScript 开发人员的路上!

VS Code 具有出色的 TypeScript 支持,并且本身内置于 TypeScript 中。你不一定要将它用于 TypeScript——几乎所有现代编辑器都有很好的 TypeScript 支持,无论是内置的还是通过插件提供的——但我确实推荐你至少在阅读本书时尝试 TypeScript。如果你确实使用其它编辑器,我还建议你启用其 TypeScript 支持。我将在第 12 章 “使用 IDE 功能”中更深入地介绍编辑器功能。

TypeScript 不是什么

既然你已经看到了 TypeScript 有多棒,我必须警告你一些限制。每个工具在某些领域都表现出色,但在其他领域也有局限性。

错误代码的补救措施

TypeScript 帮助你组织你的 JavaScript 代码,但除了强制类型安全之外,它不会对代码结构有任何特定的规定。

很好!

TypeScript是一种每个人都可以使用的语言,而不是一个有目标受众的有意见的框架。你可以使用你从JavaScript中习惯的任何架构模式来编写代码,而TypeScript将支持它们。

如果有人试图告诉你,TypeScript 强制你使用类,或者很难编写好的代码,或者其他任何代码风格的抱怨,给他们一个严厉的眼神,并告诉他们去拿一本《TypeScript 学习指南》。TypeScript 不强制代码风格的意见,比如是否使用类或函数,也不与任何特定的应用框架(如Angular,React等)相关联。

JavaScript 的扩展(主要是)

TypeScript 的设计目标明确指出它应该:

  • 与当前和未来的 ECMAScript 提案保持一致
  • 保留所有 JavaScript 代码的运行时行为

TypeScript 不会尝试改变 JavaScript 的工作方式。它的创建者们非常努力地避免添加新的代码功能,以免与 JavaScript 冲突。这种任务属于 TC39,这个负责 ECMAScript 本身的技术委员会的领域。

TypeScript 中有一些较旧的功能,它们是多年前添加的,以反映 JavaScript 代码中的常见用例。其中大多数功能要么相对不常见,要么已经不再流行,只在第14章“语法扩展”中简要介绍。我建议在大多数情况下都避免使用它们。

截至 2022 年,C39 目前正在调查为 JavaScript 添加类型注解的语法。最新的提案将它们作为不会影响运行时代码的注释,仅用于开发时系统,如 TypeScript。在将类型注解或等价物添加到 JavaScript 之前还需要很多年,因此本书中不会提及它们。

比 JavaScript 慢

有时候在互联网上,你可能会听到一些有主见的开发人员抱怨 TypeScript 在运行时比 JavaScript 慢。这种说法通常是不准确和误导的。TypeScript 对代码唯一做出的改变是,如果你要求它将你的代码编译为更早版本的 JavaScript,以支持更旧的运行时环境,比如 Internet Explorer 11。许多生产框架根本不使用 TypeScript 的编译器,而是使用单独的工具进行转译(编译的一部分,将源代码从一种编程语言转换为另一种编程语言),而 TypeScript 仅用于类型检查。

但是,TypeScript 确实会为构建代码增加一些时间。TypeScript 代码必须先编译为 JavaScript,然后大多数环境(如浏览器和 Node.js)才能运行它。大多数构建管道通常设置为对性能的影响可以忽略不计,并且较慢的 TypeScript 功能(如分析代码以查找可能的错误)与生成可运行的应用程序代码文件分开完成。

即使是看似允许直接运行 TypeScript 代码的项目,例如 ts-node 和 Deno,它们本身也会在运行 TypeScript 代码之前在内部将 TypeScript 代码转换为 JavaScript。

完成演进

Web 的发展还远未结束,因此 TypeScript也在不断发展。TypeScript 语言不断接收错误修复和功能增加,以适应不断变化的Web社区需求。本书中你将学习的 TypeScript 的基本原则将保持大致相同,但错误消息、更高级的功能和编辑器集成将会随着时间的推移不断改进。

实际上,在这本书的版本中,最新的 TypeScript 版本是 4.7.2,但在你开始阅读之时,可以肯定已经发布了更新的版本。本书中的一些TypeScript 错误消息甚至可能已经过时了!

总结

在本章中,你了解了 JavaScript 的一些主要缺点,TypeScript 在哪里发挥作用,以及如何开始使用 TypeScript:

  • JavaScript 的简要历史
  • JavaScript 的缺点:自由代价高昂、文档松散、开发工具弱
  • TypeScript 是什么:一种编程语言、类型检查器、编译器和语言服务
  • TypeScript 的优势:通过限制获得自由、精确的文档、更强大的开发工具
  • 在 TypeScript 演练场中和本地计算机上开始编写 TypeScript 代码
  • TypeScript 不是什么:修复糟糕代码的方法、JavaScript的扩展(主要是)、比JavaScript慢或已经完成演进。

现在你已经读完了这一章,你最好练习一下学到的东西 https://learningtypescript.com/from-javascript-to-typescript


如果你发现运行 TypeScript 编译器的错误,会发生什么?
你最好去抓住他们!

1

评论 (0)

取消