基础知识

欢迎来到本手册的第一页。 如果这是你第一次使用 TypeScript - 你可能需要从 入门 指南开始

JavaScript 中的每个值都有一组行为,你可以通过运行不同的操作来观察这些行为。这听起来很抽象,但作为一个简单的例子,考虑我们可能在名为 message 的变量上运行的一些操作。

¥Each and every value in JavaScript has a set of behaviors you can observe from running different operations. That sounds abstract, but as a quick example, consider some operations we might run on a variable named message.

js
// Accessing the property 'toLowerCase'
// on 'message' and then calling it
message.toLowerCase();
// Calling 'message'
message();

如果我们将其分解,第一行可运行的代码会访问一个名为 toLowerCase 的属性,然后调用它。第二个尝试直接调用 message

¥If we break this down, the first runnable line of code accesses a property called toLowerCase and then calls it. The second one tries to call message directly.

但假设我们不知道 message 的值 - 这很常见 - 我们无法可靠地说尝试运行任何这些代码会得到什么结果。每个操作的行为完全取决于我们最初拥有的值。

¥But assuming we don’t know the value of message - and that’s pretty common - we can’t reliably say what results we’ll get from trying to run any of this code. The behavior of each operation depends entirely on what value we had in the first place.

  • message 可以调用吗?

    ¥Is message callable?

  • 它上面是否有一个名为 toLowerCase 的属性?

    ¥Does it have a property called toLowerCase on it?

  • 如果是这样,toLowerCase 甚至可以调用吗?

    ¥If it does, is toLowerCase even callable?

  • 如果这两个值都是可调用的,它们会返回什么?

    ¥If both of these values are callable, what do they return?

这些问题的答案通常是我们在编写 JavaScript 时牢记在心的事情,我们必须希望我们得到了正确的所有细节。

¥The answers to these questions are usually things we keep in our heads when we write JavaScript, and we have to hope we got all the details right.

假设 message 是按以下方式定义的。

¥Let’s say message was defined in the following way.

js
const message = "Hello World!";

正如你可能猜到的,如果我们尝试运行 message.toLowerCase(),我们将只得到相同的小写字符串。

¥As you can probably guess, if we try to run message.toLowerCase(), we’ll get the same string only in lower-case.

那第二行代码呢?如果你熟悉 JavaScript,就会知道这会失败并出现异常:

¥What about that second line of code? If you’re familiar with JavaScript, you’ll know this fails with an exception:

txt
TypeError: message is not a function

如果我们能避免这样的错误,那就太好了。

¥It’d be great if we could avoid mistakes like this.

当我们运行代码时,JavaScript 运行时选择要执行的操作的方式是确定值的类型 - 它有什么样的行为和能力。这就是 TypeError 所暗示的一部分 - 它表示字符串 "Hello World!" 不能作为函数调用。

¥When we run our code, the way that our JavaScript runtime chooses what to do is by figuring out the type of the value - what sorts of behaviors and capabilities it has. That’s part of what that TypeError is alluding to - it’s saying that the string "Hello World!" cannot be called as a function.

对于某些值,例如基础类型 stringnumber,我们可以在运行时使用 typeof 运算符识别它们的类型。但是对于其他的东西,比如函数,没有相应的运行时机制来识别它们的类型。例如,考虑这个函数:

¥For some values, such as the primitives string and number, we can identify their type at runtime using the typeof operator. But for other things like functions, there’s no corresponding runtime mechanism to identify their types. For example, consider this function:

js
function fn(x) {
return x.flip();
}

我们可以通过阅读代码观察到,这个函数只有在给定一个具有可调用 flip 属性的对象时才能工作,但 JavaScript 不会以我们可以在代码运行时检查的方式显示这些信息。在纯 JavaScript 中,判断 fn 对特定值做了什么的唯一方法是调用它并查看会发生什么。这种行为使得很难在代码运行之前预测代码将做什么,这意味着在编写代码时更难知道代码将要做什么。

¥We can observe by reading the code that this function will only work if given an object with a callable flip property, but JavaScript doesn’t surface this information in a way that we can check while the code is running. The only way in pure JavaScript to tell what fn does with a particular value is to call it and see what happens. This kind of behavior makes it hard to predict what the code will do before it runs, which means it’s harder to know what your code is going to do while you’re writing it.

这样看,类型就是描述哪些值可以传递给 fn,哪些会崩溃的概念。JavaScript 只真正提供动态类型 - 运行代码看看会发生什么。

¥Seen in this way, a type is the concept of describing which values can be passed to fn and which will crash. JavaScript only truly provides dynamic typing - running the code to see what happens.

另一种方法是使用静态类型系统在代码运行之前预测代码预期执行的操作。

¥The alternative is to use a static type system to make predictions about what the code is expected to do before it runs.

静态类型检查

¥Static type-checking

回想一下我们之前尝试将 string 作为函数调用而得到的 TypeError。大多数人不喜欢在运行代码时出现任何类型的错误 - 这些被认为是错误!当我们编写新代码时,我们会尽力避免引入新的错误。

¥Think back to that TypeError we got earlier from trying to call a string as a function. Most people don’t like to get any sorts of errors when running their code - those are considered bugs! And when we write new code, we try our best to avoid introducing new bugs.

如果我们只添加一点代码,保存我们的文件,重新运行代码,并立即看到错误,我们可能能够快速隔离问题;但情况并非总是如此。我们可能没有对这个功能进行足够彻底的测试,所以我们可能永远不会真正遇到可能抛出的潜在错误!或者,如果我们有幸目睹了这个错误,我们可能最终会进行大规模的重构并添加许多我们不得不挖掘的不同代码。

¥If we add just a bit of code, save our file, re-run the code, and immediately see the error, we might be able to isolate the problem quickly; but that’s not always the case. We might not have tested the feature thoroughly enough, so we might never actually run into a potential error that would be thrown! Or if we were lucky enough to witness the error, we might have ended up doing large refactorings and adding a lot of different code that we’re forced to dig through.

理想情况下,我们可以有一个工具来帮助我们在代码运行之前找到这些错误。这就是像 TypeScript 这样的静态类型检查器所做的。静态类型系统描述了当我们运行程序时我们的值的形状和行为。像 TypeScript 这样的类型检查器使用这些信息并告诉我们什么时候事情可能会出轨。

¥Ideally, we could have a tool that helps us find these bugs before our code runs. That’s what a static type-checker like TypeScript does. Static types systems describe the shapes and behaviors of what our values will be when we run our programs. A type-checker like TypeScript uses that information and tells us when things might be going off the rails.

ts
const message = "hello!";
 
message();
This expression is not callable. Type 'String' has no call signatures.2349This expression is not callable. Type 'String' has no call signatures.
Try

在我们首先运行代码之前,使用 TypeScript 运行最后一个示例会给我们一个错误消息。

¥Running that last sample with TypeScript will give us an error message before we run the code in the first place.

非异常故障

¥Non-exception Failures

到目前为止,我们一直在讨论某些事情,例如运行时错误 - JavaScript 运行时告诉我们它认为某些事情是无意义的情况。出现这些情况是因为 ECMAScript 规范 明确说明了语言在遇到意外情况时应该如何表现。

¥So far we’ve been discussing certain things like runtime errors - cases where the JavaScript runtime tells us that it thinks something is nonsensical. Those cases come up because the ECMAScript specification has explicit instructions on how the language should behave when it runs into something unexpected.

例如,规范说尝试调用不可调用的东西应该会引发错误。也许这听起来像 “明显的行为”,但你可以想象访问对象上不存在的属性也会引发错误。相反,JavaScript 为我们提供了不同的行为并返回值 undefined

¥For example, the specification says that trying to call something that isn’t callable should throw an error. Maybe that sounds like “obvious behavior”, but you could imagine that accessing a property that doesn’t exist on an object should throw an error too. Instead, JavaScript gives us different behavior and returns the value undefined:

js
const user = {
name: "Daniel",
age: 26,
};
user.location; // returns undefined

最终,静态类型系统必须调用其系统中应将哪些代码标记为错误,即使是不会立即抛出错误的 “valid” JavaScript。在 TypeScript 中,以下代码会产生关于 location 未定义的错误:

¥Ultimately, a static type system has to make the call over what code should be flagged as an error in its system, even if it’s “valid” JavaScript that won’t immediately throw an error. In TypeScript, the following code produces an error about location not being defined:

ts
const user = {
name: "Daniel",
age: 26,
};
 
user.location;
Property 'location' does not exist on type '{ name: string; age: number; }'.2339Property 'location' does not exist on type '{ name: string; age: number; }'.
Try

虽然有时这意味着在你可以表达的内容上进行权衡,但其目的是捕捉我们程序中的合法错误。TypeScript 捕获了很多合法的错误。

¥While sometimes that implies a trade-off in what you can express, the intent is to catch legitimate bugs in our programs. And TypeScript catches a lot of legitimate bugs.

例如:错别字,

¥For example: typos,

ts
const announcement = "Hello World!";
 
// How quickly can you spot the typos?
announcement.toLocaleLowercase();
announcement.toLocalLowerCase();
 
// We probably meant to write this...
announcement.toLocaleLowerCase();
Try

未调用的函数,

¥uncalled functions,

ts
function flipCoin() {
// Meant to be Math.random()
return Math.random < 0.5;
Operator '<' cannot be applied to types '() => number' and 'number'.2365Operator '<' cannot be applied to types '() => number' and 'number'.
}
Try

或基本逻辑错误。

¥or basic logic errors.

ts
const value = Math.random() < 0.5 ? "a" : "b";
if (value !== "a") {
// ...
} else if (value === "b") {
This comparison appears to be unintentional because the types '"a"' and '"b"' have no overlap.2367This comparison appears to be unintentional because the types '"a"' and '"b"' have no overlap.
// Oops, unreachable
}
Try

工具类型

¥Types for Tooling

当我们在代码中出错时,TypeScript 可以捕获错误。这很好,但 TypeScript 也可以从一开始就阻止我们犯这些错误。

¥TypeScript can catch bugs when we make mistakes in our code. That’s great, but TypeScript can also prevent us from making those mistakes in the first place.

类型检查器具有检查诸如我们是否正在访问变量和其他属性的正确属性之类的信息。一旦有了这些信息,它还可以开始建议你可能想要使用哪些属性。

¥The type-checker has information to check things like whether we’re accessing the right properties on variables and other properties. Once it has that information, it can also start suggesting which properties you might want to use.

这意味着 TypeScript 也可以用于编辑代码,核心类型检查器可以在你在编辑器中键入时提供错误消息和代码完成。这是人们在谈论 TypeScript 工具时经常提到的部分内容。

¥That means TypeScript can be leveraged for editing code too, and the core type-checker can provide error messages and code completion as you type in the editor. That’s part of what people often refer to when they talk about tooling in TypeScript.

ts
import express from "express";
const app = express();
 
app.get("/", function (req, res) {
res.sen
         
});
 
app.listen(3000);
Try

TypeScript 非常重视工具,这超出了你键入时的完成和错误。支持 TypeScript 的编辑器可以提供 “快速修复” 以自动修复错误、重构以轻松重新组织代码,以及用于跳转到变量定义或查找对给定变量的所有引用的有用导航功能。所有这些都建立在类型检查器之上,并且是完全跨平台的,所以很可能是 你最喜欢的编辑器支持 TypeScript

¥TypeScript takes tooling seriously, and that goes beyond completions and errors as you type. An editor that supports TypeScript can deliver “quick fixes” to automatically fix errors, refactorings to easily re-organize code, and useful navigation features for jumping to definitions of a variable, or finding all references to a given variable. All of this is built on top of the type-checker and is fully cross-platform, so it’s likely that your favorite editor has TypeScript support available.

tsc,TypeScript 编译器

¥tsc, the TypeScript compiler

我们一直在谈论类型检查,但我们还没有使用我们的类型检查器。让我们熟悉一下我们的新朋友 tsc,TypeScript 编译器。首先,我们需要通过 npm 获取它。

¥We’ve been talking about type-checking, but we haven’t yet used our type-checker. Let’s get acquainted with our new friend tsc, the TypeScript compiler. First we’ll need to grab it via npm.

sh
npm install -g typescript

这将全局安装 TypeScript 编译器 tsc。如果你希望从本地 node_modules 包运行 tsc,则可以使用 npx 或类似工具。

¥This installs the TypeScript Compiler tsc globally. You can use npx or similar tools if you’d prefer to run tsc from a local node_modules package instead.

现在让我们移至一个空文件夹并尝试编写我们的第一个 TypeScript 程序:hello.ts

¥Now let’s move to an empty folder and try writing our first TypeScript program: hello.ts:

ts
// Greets the world.
console.log("Hello world!");
Try

请注意这里没有多余的装饰;这个 “你好世界” 程序看起来与你用 JavaScript 编写的 “你好世界” 程序完全相同。现在让我们通过运行 typescript 包为我们安装的命令 tsc 来检查它。

¥Notice there are no frills here; this “hello world” program looks identical to what you’d write for a “hello world” program in JavaScript. And now let’s type-check it by running the command tsc which was installed for us by the typescript package.

sh
tsc hello.ts

Tada!

等等,到底 “tada” 什么?我们运行了 tsc,但什么也没发生!好吧,没有类型错误,所以我们没有在控制台中得到任何输出,因为没有什么要报告的。

¥Wait, “tada” what exactly? We ran tsc and nothing happened! Well, there were no type errors, so we didn’t get any output in our console since there was nothing to report.

但再检查一下 - 我们得到了一些文件输出。如果我们查看当前目录,我们会在 hello.ts 旁边看到一个 hello.js 文件。这是 tsc 编译或转换为纯 JavaScript 文件后我们的 hello.ts 文件的输出。如果我们检查内容,我们会看到 TypeScript 在处理 .ts 文件后会吐出什么:

¥But check again - we got some file output instead. If we look in our current directory, we’ll see a hello.js file next to hello.ts. That’s the output from our hello.ts file after tsc compiles or transforms it into a plain JavaScript file. And if we check the contents, we’ll see what TypeScript spits out after it processes a .ts file:

js
// Greets the world.
console.log("Hello world!");

在这种情况下,TypeScript 几乎不需要转换,所以它看起来和我们写的一样。编译器试图触发看起来像人会写的东西的干净可读的代码。虽然这并不总是那么容易,但 TypeScript 会始终如一地缩进,注意我们的代码何时跨越不同的代码行,并试图保留注释。

¥In this case, there was very little for TypeScript to transform, so it looks identical to what we wrote. The compiler tries to emit clean readable code that looks like something a person would write. While that’s not always so easy, TypeScript indents consistently, is mindful of when our code spans across different lines of code, and tries to keep comments around.

如果我们确实引入了类型检查错误怎么办?让我们重写 hello.ts

¥What about if we did introduce a type-checking error? Let’s rewrite hello.ts:

ts
// This is an industrial-grade general-purpose greeter function:
function greet(person, date) {
console.log(`Hello ${person}, today is ${date}!`);
}
 
greet("Brendan");
Try

如果我们再次运行 tsc hello.ts,请注意我们在命令行上收到错误!

¥If we run tsc hello.ts again, notice that we get an error on the command line!

txt
Expected 2 arguments, but got 1.

TypeScript 告诉我们,我们忘记将参数传递给 greet 函数,这是理所当然的。到目前为止,我们只编写了标准的 JavaScript,但类型检查仍然能够发现我们代码的问题。感谢 TypeScript!

¥TypeScript is telling us we forgot to pass an argument to the greet function, and rightfully so. So far we’ve only written standard JavaScript, and yet type-checking was still able to find problems with our code. Thanks TypeScript!

使用错误触发

¥Emitting with Errors

从上一个示例中你可能没有注意到的一件事是我们的 hello.js 文件再次更改。如果我们打开该文件,我们会看到内容与我们的输入文件看起来基本相同。考虑到 tsc 报告了关于我们代码的错误,这可能有点令人惊讶,但这是基于 TypeScript 的核心价值之一:很多时候,你会比 TypeScript 更了解。

¥One thing you might not have noticed from the last example was that our hello.js file changed again. If we open that file up then we’ll see that the contents still basically look the same as our input file. That might be a bit surprising given the fact that tsc reported an error about our code, but this is based on one of TypeScript’s core values: much of the time, you will know better than TypeScript.

重申一下,类型检查代码限制了你可以运行的程序种类,因此需要权衡类型检查器认为可以接受的类型。大多数时候没关系,但在某些情况下,这些检查会妨碍你。例如,假设你将 JavaScript 代码迁移到 TypeScript 并引入类型检查错误。最终,你将开始为类型检查器清理东西,但原始的 JavaScript 代码已经可以工作了!为什么要将其转换为 TypeScript 会阻止你运行它?

¥To reiterate from earlier, type-checking code limits the sorts of programs you can run, and so there’s a tradeoff on what sorts of things a type-checker finds acceptable. Most of the time that’s okay, but there are scenarios where those checks get in the way. For example, imagine yourself migrating JavaScript code over to TypeScript and introducing type-checking errors. Eventually you’ll get around to cleaning things up for the type-checker, but that original JavaScript code was already working! Why should converting it over to TypeScript stop you from running it?

所以 TypeScript 不会妨碍你。当然,随着时间的推移,你可能希望对错误更加防御,并使 TypeScript 的行为更加严格。在这种情况下,你可以使用 noEmitOnError 编译器选项。尝试更改你的 hello.ts 文件并使用该标志运行 tsc

¥So TypeScript doesn’t get in your way. Of course, over time, you may want to be a bit more defensive against mistakes, and make TypeScript act a bit more strictly. In that case, you can use the noEmitOnError compiler option. Try changing your hello.ts file and running tsc with that flag:

sh
tsc --noEmitOnError hello.ts

你会注意到 hello.js 永远不会更新。

¥You’ll notice that hello.js never gets updated.

显式的类型

¥Explicit Types

到目前为止,我们还没有告诉 TypeScript persondate 是什么。让我们编辑代码来告诉 TypeScript person 是一个 string,而 date 应该是一个 Date 对象。我们还将在 date 上使用 toDateString() 方法。

¥Up until now, we haven’t told TypeScript what person or date are. Let’s edit the code to tell TypeScript that person is a string, and that date should be a Date object. We’ll also use the toDateString() method on date.

ts
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
Try

我们所做的是在 persondate 上添加类型注释来描述可以使用哪些类型的值来调用 greet。你可以将该签名读作“greet 采用 string 类型的 personDate 类型的 date”。

¥What we did was add type annotations on person and date to describe what types of values greet can be called with. You can read that signature as ”greet takes a person of type string, and a date of type Date“.

有了这个,TypeScript 可以告诉我们 greet 可能被错误调用的其他情况。例如…

¥With this, TypeScript can tell us about other cases where greet might have been called incorrectly. For example…

ts
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
 
greet("Maddison", Date());
Argument of type 'string' is not assignable to parameter of type 'Date'.2345Argument of type 'string' is not assignable to parameter of type 'Date'.
Try

嗯?TypeScript 在我们的第二个参数上报告了错误,但为什么呢?

¥Huh? TypeScript reported an error on our second argument, but why?

也许令人惊讶的是,在 JavaScript 中调用 Date() 返回一个 string。另一方面,用 new Date() 构造一个 Date 实际上给了我们所期望的结果。

¥Perhaps surprisingly, calling Date() in JavaScript returns a string. On the other hand, constructing a Date with new Date() actually gives us what we were expecting.

无论如何,我们可以快速修复错误:

¥Anyway, we can quickly fix up the error:

ts
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
 
greet("Maddison", new Date());
Try

请记住,我们并不总是必须编写显式类型注释。在许多情况下,TypeScript 甚至可以为我们推断(或 “弄清楚”)类型,即使我们省略它们。

¥Keep in mind, we don’t always have to write explicit type annotations. In many cases, TypeScript can even just infer (or “figure out”) the types for us even if we omit them.

ts
let msg = "hello there!";
let msg: string
Try

即使我们没有告诉 TypeScript msgstring 类型,它也能够弄清楚这一点。这是一个特性,当类型系统最终会推断出相同的类型时,最好不要添加注释。

¥Even though we didn’t tell TypeScript that msg had the type string it was able to figure that out. That’s a feature, and it’s best not to add annotations when the type system would end up inferring the same type anyway.

注意:如果你将鼠标悬停在该单词上,则上一个代码示例中的消息气泡是你的编辑器将显示的内容。

¥Note: The message bubble inside the previous code sample is what your editor would show if you had hovered over the word.

擦除的类型

¥Erased Types

让我们看看当我们用 tsc 编译上面的函数 greet 以输出 JavaScript 时会发生什么:

¥Let’s take a look at what happens when we compile the above function greet with tsc to output JavaScript:

ts
"use strict";
function greet(person, date) {
console.log("Hello ".concat(person, ", today is ").concat(date.toDateString(), "!"));
}
greet("Maddison", new Date());
 
Try

这里注意两点:

¥Notice two things here:

  1. 我们的 persondate 参数不再有类型注释。

    ¥Our person and date parameters no longer have type annotations.

  2. 我们的 “模板字符串” - 使用反引号的字符串(``` 字符) - 被转换为带有连接的纯字符串。

    ¥Our “template string” - that string that used backticks (the ` character) - was converted to plain strings with concatenations.

稍后会详细介绍第二点,但现在让我们关注第一点。类型注释不是 JavaScript 的一部分(或者 ECMAScript 是迂腐的),所以实际上没有任何浏览器或其他运行时可以在未经修改的情况下运行 TypeScript。这就是为什么 TypeScript 首先需要编译器 - 它需要某种方法来删除或转换任何特定于 TypeScript 的代码,以便你可以运行它。大多数特定于 TypeScript 的代码都被删除了,同样地,我们的类型注释也被完全删除了。

¥More on that second point later, but let’s now focus on that first point. Type annotations aren’t part of JavaScript (or ECMAScript to be pedantic), so there really aren’t any browsers or other runtimes that can just run TypeScript unmodified. That’s why TypeScript needs a compiler in the first place - it needs some way to strip out or transform any TypeScript-specific code so that you can run it. Most TypeScript-specific code gets erased away, and likewise, here our type annotations were completely erased.

记住:类型注释永远不会改变程序的运行时行为。

¥Remember: Type annotations never change the runtime behavior of your program.

降级

¥Downleveling

与上面的另一个区别是我们的模板字符串是从

¥One other difference from the above was that our template string was rewritten from

js
`Hello ${person}, today is ${date.toDateString()}!`;

¥to

js
"Hello ".concat(person, ", today is ").concat(date.toDateString(), "!");

为什么会这样?

¥Why did this happen?

模板字符串是 ECMAScript 版本 ECMAScript 2015(又名 ECMAScript 6、ES2015、ES6 等 - 不要问)的一项功能。TypeScript 能够将代码从较新版本的 ECMAScript 重写为较旧的版本,例如 ECMAScript 3 或 ECMAScript 5(又名 ES3 和 ES5)。从 ECMAScript 的新版本或 “higher” 版本向下移动到旧版本或 “lower” 版本的过程有时称为降级。

¥Template strings are a feature from a version of ECMAScript called ECMAScript 2015 (a.k.a. ECMAScript 6, ES2015, ES6, etc. - don’t ask). TypeScript has the ability to rewrite code from newer versions of ECMAScript to older ones such as ECMAScript 3 or ECMAScript 5 (a.k.a. ES3 and ES5). This process of moving from a newer or “higher” version of ECMAScript down to an older or “lower” one is sometimes called downleveling.

默认情况下,TypeScript 以 ES3 为目标,这是一个非常旧的 ECMAScript 版本。通过使用 target 选项,我们可以选择更新一点的东西。使用 --target es2015 运行将 TypeScript 更改为以 ECMAScript 2015 为目标,这意味着代码应该能够在任何支持 ECMAScript 2015 的地方运行。所以运行 tsc --target es2015 hello.ts 会给我们以下输出:

¥By default TypeScript targets ES3, an extremely old version of ECMAScript. We could have chosen something a little bit more recent by using the target option. Running with --target es2015 changes TypeScript to target ECMAScript 2015, meaning code should be able to run wherever ECMAScript 2015 is supported. So running tsc --target es2015 hello.ts gives us the following output:

js
function greet(person, date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", new Date());

虽然默认目标是 ES3,但当前绝大多数浏览器都支持 ES2015。因此,大多数开发者可以安全地将 ES2015 或更高版本指定为目标,除非与某些古老的浏览器的兼容性很重要。

¥While the default target is ES3, the great majority of current browsers support ES2015. Most developers can therefore safely specify ES2015 or above as a target, unless compatibility with certain ancient browsers is important.

严格性

¥Strictness

不同的用户使用 TypeScript 在类型检查器中寻找不同的东西。有些人正在寻找一种更宽松的选择加入体验,它可以帮助验证他们程序的某些部分,并且仍然拥有不错的工具。这是 TypeScript 的默认体验,其中类型是可选的,推断采用最宽松的类型,并且不检查潜在的 null/undefined 值。就像 tsc 在面对错误时触发的一样,这些默认设置是为了不妨碍你。如果你要迁移现有的 JavaScript,那么这可能是理想的第一步。

¥Different users come to TypeScript looking for different things in a type-checker. Some people are looking for a more loose opt-in experience which can help validate only some parts of their program, and still have decent tooling. This is the default experience with TypeScript, where types are optional, inference takes the most lenient types, and there’s no checking for potentially null/undefined values. Much like how tsc emits in the face of errors, these defaults are put in place to stay out of your way. If you’re migrating existing JavaScript, that might be a desirable first step.

相比之下,许多用户更喜欢让 TypeScript 尽可能多地立即验证,这就是该语言也提供严格设置的原因。这些严格性设置将静态类型检查从开关(无论是否检查你的代码)变成更接近调节器的东西。你把这个调节器调得越高,TypeScript 就会越多地为你检查。这可能需要一些额外的工作,但一般来说,从长远来看,它会为自己付出代价,并且可以进行更彻底的检查和更准确的工具。如果可能,新的代码库应始终打开这些严格性检查。

¥In contrast, a lot of users prefer to have TypeScript validate as much as it can straight away, and that’s why the language provides strictness settings as well. These strictness settings turn static type-checking from a switch (either your code is checked or not) into something closer to a dial. The further you turn this dial up, the more TypeScript will check for you. This can require a little extra work, but generally speaking it pays for itself in the long run, and enables more thorough checks and more accurate tooling. When possible, a new codebase should always turn these strictness checks on.

TypeScript 有几个可以打开或关闭的类型检查严格标志,除非另有说明,否则我们所有的示例都将在启用所有这些标志的情况下编写。CLI 中的 strict 标志或 tsconfig.json 中的 "strict": true 会同时将它们全部打开,但我们可以单独选择退出它们。你应该知道的两个最大的是 noImplicitAnystrictNullChecks

¥TypeScript has several type-checking strictness flags that can be turned on or off, and all of our examples will be written with all of them enabled unless otherwise stated. The strict flag in the CLI, or "strict": true in a tsconfig.json toggles them all on simultaneously, but we can opt out of them individually. The two biggest ones you should know about are noImplicitAny and strictNullChecks.

noImplicitAny

回想一下,在某些地方,TypeScript 不会尝试为我们推断类型,而是退回到最宽松的类型:any。这还不是最糟糕的事情 - 毕竟,回退到 any 就是简单的 JavaScript 体验。

¥Recall that in some places, TypeScript doesn’t try to infer types for us and instead falls back to the most lenient type: any. This isn’t the worst thing that can happen - after all, falling back to any is just the plain JavaScript experience anyway.

然而,使用 any 通常会破坏使用 TypeScript 的初衷。你的程序类型越多,你获得的验证和工具就越多,这意味着你在编写代码时遇到的错误就越少。打开 noImplicitAny 标志将对任何类型隐式推断为 any 的变量触发错误。

¥However, using any often defeats the purpose of using TypeScript in the first place. The more typed your program is, the more validation and tooling you’ll get, meaning you’ll run into fewer bugs as you code. Turning on the noImplicitAny flag will issue an error on any variables whose type is implicitly inferred as any.

strictNullChecks

默认情况下,像 nullundefined 这样的值可以分配给任何其他类型。这可以使编写一些代码变得更容易,但是忘记处理 nullundefined 是世界上无数错误的原因 - 有些人认为它是 十亿美元的错误strictNullChecks 标志使处理 nullundefined 更加明确,让我们不必担心是否忘记处理 nullundefined

¥By default, values like null and undefined are assignable to any other type. This can make writing some code easier, but forgetting to handle null and undefined is the cause of countless bugs in the world - some consider it a billion dollar mistake! The strictNullChecks flag makes handling null and undefined more explicit, and spares us from worrying about whether we forgot to handle null and undefined.