跳到主要内容
Deno 2.4 已发布,带来 deno bundle、字节/文本导入、OTel 稳定版等新特性
了解更多
Intro to Typescript

TypeScript 温和入门

对于许多通过 JavaScript 接触编程的人来说,很容易爱上它低门槛和多功能的特性。JavaScript 可以在浏览器中运行,可以在记事本中编写,逐行解释,无需复杂的编译或工具。JavaScript 通过让来自不同背景的开发者都能轻松上手并开始编码,从而实现了软件开发的民主化。但 JavaScript 这种宽容的特性也增加了犯错和引入 bug 的可能性。

考虑以下 JavaScript 程序

function add(a, b) {
  return a + b;
}

console.log(add(1, 2)); // 3

这是一个简单的加法程序,旨在接收两个数字并将它们相加,然后返回结果。当我们使用非数字值调用此函数时,意外的、通常是不可预测的事情可能会开始发生。

我们实际上只希望使用数字来调用这个函数——否则就没有意义——事实上,如果你传递给它非数字值,你最终会得到常常是怪异且不可预测的结果

console.log(add(1, "2")); // 12
console.log(add(1, true)); // 2
console.log(add(1, "hello")); // 1hello

JavaScript 是一种动态类型语言。这意味着我们存储在变量中的数据类型可以在运行时改变。这使得在 JavaScript 代码中表达我们的意图变得复杂——即我们的 add 函数应该只用数字调用。

我们在这里看到的情况被称为类型强制转换——JavaScript 会自动将值从一种数据类型转换为另一种数据类型。这可以通过函数和运算符的显式使用来发生,或者在 JavaScript 在特定上下文中期望某种类型的值时隐式发生。隐式类型强制转换有时会导致意外结果,尤其是在复杂的表达式中。以下是一些示例

console.log(1 + "2"); // "12" (number 1 is converted to string)
console.log("2" + 1); // "21" (number 1 is converted to string)

console.log("5" - 1); // 4 (string "5" is converted to number)
console.log("5" * "2"); // 10 (both strings are converted to numbers)

console.log(0 == false); // true (number 0 is converted to false)
console.log(0 === false); // false (no type coercion, different types)

是不是很令人困惑?!

在 JavaScript 代码库中,我们能做的最好方法是向程序添加一些守卫检查(guard checks),以便在提供无效值时抛出错误。仅依靠这些运行时检查来保护我们免受错误影响的问题在于,我们通常会和用户同时发现代码中引入的 bug。那么,我们如何保护自己免受这种常常令人困惑的 JavaScript 行为的影响呢?

TypeScript 伸出援手

TypeScript 可以通过明确我们期望在何处使用何种类型来帮助我们描述代码的意图。TypeScript 是 JavaScript 的一个超集,它为该语言添加了额外的类型信息。当你编译 TypeScript 时,会生成可以在任何地方运行的 JavaScript 代码,因此可以非常容易地增量使用它来改善你的开发体验,而无需重新构建所有软件。

类型允许你在代码运行之前捕获错误。如果你不小心将错误类型的值分配给变量,你将收到编译错误。类型还使你的代码更易于阅读,因为你可以明确声明你期望的值类型。我们还可以使用工具来使类型更加强大。当你正确使用类型时,代码编辑器和 IDE 都内置了工具来帮助你编写代码时进行自动补全。

如何添加类型注解?

在 TypeScript 中,你可以通过在函数参数名后面添加冒号(:)和所需的类型来添加类型注解。如果我们将之前的 add 示例扩展为 TypeScript,它会是这样的

function add(x: number, y: number) {
  return x + y;
}

console.log(add(1, 2)); // 3

这是我们能对代码做出的最简单的改变,使其更加健壮。现在编译器知道,任何尝试使用非两个数字调用 add() 的代码都将在运行时失败,因此它会在编译时抛出错误,告诉你程序无效。

例如,如果我们尝试使用一个数字和一个字符串调用 add,我们将得到一个编译器错误

compiler error in VSCode

TypeScript 足够智能,可以根据你提供的信息为你推断程序中的某些类型,我们称之为“类型推断”。如果我们以上述示例并将其展开,添加所有我们可以添加的类型注解,它会是这样的

function add(x: number, y: number): number {
  return x + y;
}

const result: number = add(1, 1);
console.log(result);

在这里,我们明确为函数参数、add 函数的返回类型以及变量 result 添加了注解。通常,开发者会添加“刚刚好”的类型注解来告诉 TypeScript 编译器正在发生什么,然后让它推断其余部分。在我们最初的示例中,通过为 xy 参数添加类型注解,TypeScript 编译器可以检查代码并意识到我们只添加了两个数字,因此会推断出函数返回类型以及变量 result 的类型都是 number

即使你只用 TypeScript 来注解函数参数,你也会立即从代码中消除一整类错误。

将你的 TypeScript 转换回 JavaScript

如果你的项目使用 Node 构建,你需要添加 typescript 包并运行 tsc 编译器工具。我们已经撰写了一篇关于配置 TypeScript 编译器的介绍

Deno 内置了 TypeScript 编译器,因此如果你正在使用 Deno,你不需要任何其他配置或工具。Deno 开箱即用支持 TypeScript,在我们执行你的源代码时会自动将 TypeScript 转换为 JavaScript。

在客户端,你可以使用 Vite,一个与我们理念相投的工具,它为你进行类似的透明编译,所以如果你是前端开发者,你的代码中仍然可以享受到 TypeScript 带来的乐趣。

接下来

在下一篇 Deno Bite 中,我们将讨论你在 TS 代码中需要的常见类型,以及如何使用它们来构建更复杂的类型,使你的代码清晰且无 bug!

有没有你希望我们涵盖的 TypeScript 主题?请在 TwitterDiscord 上告诉我们!