在 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 itmessage.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可以调用吗?- 它有一个名为
toLowerCase的属性吗? - 如果是这样,
toLowerCase甚至能被调用吗? - 如果这两个值都是可调用的,它们会返回什么?
这些问题的答案通常是我们在编写 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.
jsconst 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:
txtTypeError: 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.
对于某些值,例如基本类型 string 和 number,我们可以使用 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:
jsfunction 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.
从这个角度看,type 是描述哪些值可以传递给 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 type 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.
tsTryconstmessage = "hello!";This expression is not callable. Type 'String' has no call signatures.2349This expression is not callable. Type 'String' has no call signatures.(); message
在我们首先运行代码之前,使用 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:
jsconst user = {name: "Daniel",age: 26,};user.location; // returns undefined
最终,静态类型系统必须决定哪些代码应该在其系统中被标记为错误,即使这些代码是“有效的”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:
tsTryconstuser = {name : "Daniel",age : 26,};Property 'location' does not exist on type '{ name: string; age: number; }'.2339Property 'location' does not exist on type '{ name: string; age: number; }'.user .; location
虽然有时这意味着在你可以表达的内容上有所取舍,但目的是为了捕捉我们程序中的合法错误。而 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,
tsTryconstannouncement = "Hello World!";// How quickly can you spot the typos?announcement .toLocaleLowercase ();announcement .toLocalLowerCase ();// We probably meant to write this...announcement .toLocaleLowerCase ();
未调用的函数,
🌐 uncalled functions,
tsTryfunctionflipCoin () {// Meant to be Math.random()returnOperator '<' cannot be applied to types '() => number' and 'number'.2365Operator '<' cannot be applied to types '() => number' and 'number'.Math .random < 0.5;}
或基本逻辑错误。
🌐 or basic logic errors.
tsTryconstvalue =Math .random () < 0.5 ? "a" : "b";if (value !== "a") {// ...} else if (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.value === "b") {// Oops, unreachable}
工具类型
🌐 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.
tsTryimportexpress from "express";constapp =express ();app .get ("/", function (req ,res ) {res .sen });app .listen (3000);
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.
shnpm install -g typescript
这会在全局安装 TypeScript 编译器
tsc。 如果你更希望从本地node_modules包中运行tsc,可以使用npx或类似工具。
现在让我们进入一个空文件夹,并尝试编写我们的第一个 TypeScript 程序:hello.ts:
🌐 Now let’s move to an empty folder and try writing our first TypeScript program: hello.ts:
tsTry// Greets the world.console .log ("Hello world!");
请注意,这里没有任何花哨的东西;这个“hello world”程序看起来与你在 JavaScript 中编写的“hello world”程序一模一样。现在,让我们通过运行命令 tsc 来进行类型检查,该命令是由 typescript 包为我们安装的。
🌐 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.
shtsc 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.
但请再检查一次——我们得到了一些 file 输出。如果我们查看当前目录,会看到 hello.js 文件在 hello.ts 文件旁边。那是我们的 hello.ts 文件在 tsc 编译 或 转换 成普通 JavaScript 文件后的输出。如果查看内容,我们会看到 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:
tsTry// This is an industrial-grade general-purpose greeter function:functiongreet (person ,date ) {console .log (`Hello ${person }, today is ${date }!`);}greet ("Brendan");
如果我们再次运行 tsc hello.ts,注意我们会在命令行上收到一个错误!
🌐 If we run tsc hello.ts again, notice that we get an error on the command line!
txtExpected 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:
shtsc --noEmitOnError hello.ts
你会注意到 hello.js 从未被更新。
🌐 You’ll notice that hello.js never gets updated.
显式的类型
🌐 Explicit Types
到目前为止,我们还没有告诉 TypeScript person 或 date 是什么。让我们编辑代码,告诉 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.
tsTryfunctiongreet (person : string,date :Date ) {console .log (`Hello ${person }, today is ${date .toDateString ()}!`);}
我们所做的是在 person 和 date 上添加 类型注解,以描述 greet 可以接受什么类型的值。你可以将该签名理解为:“greet 接受一个类型为 string 的 person,以及一个类型为 Date 的 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…
tsTryfunctiongreet (person : string,date :Date ) {console .log (`Hello ${person }, today is ${date .toDateString ()}!`);}Argument of type 'string' is not assignable to parameter of type 'Date'.2345Argument of type 'string' is not assignable to parameter of type 'Date'.greet ("Maddison",Date ());
啊? 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:
tsTryfunctiongreet (person : string,date :Date ) {console .log (`Hello ${person }, today is ${date .toDateString ()}!`);}greet ("Maddison", newDate ());
请记住,我们并不总是必须写显式的类型注解。在许多情况下,即使我们省略类型,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.
tsTryletmsg = "hello there!";
即使我们没有告诉 TypeScript msg 的类型是 string,它仍然能够自己推断出来。这是一个特性,最好不要在类型系统本来就会推断出相同类型时添加注解。
🌐 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.
注意:在上一个代码示例中的消息气泡,就是如果你将鼠标悬停在该单词上时,编辑器会显示的内容。
擦除的类型
🌐 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:
tsTry"use strict";function greet(person, date) {console.log("Hello ".concat(person, ", today is ").concat(date.toDateString(), "!"));}greet("Maddison", new Date());
这里注意两点:
🌐 Notice two things here:
- 我们的
person和date参数不再有类型注解。 - 我们的“模板字符串”——那个使用反引号(
字符)的字符串——被转换成了使用连接的普通字符串。
稍后我们会再谈第二点,但现在让我们先关注第一点。类型注解不是 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.
记住:类型注解永远不会改变程序的运行时行为。
降级
🌐 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(也叫 ES5)。 从较新或“高版本”的 ECMAScript 降到较旧或“低版本”的这一过程,有时被称为 downleveling。
🌐 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. 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 的目标是 ES5,这是一个极其古老的 ECMAScript 版本。我们可以通过使用 target 选项选择一个更新一些的版本。使用 --target es2015 运行会将 TypeScript 的目标更改为 ECMAScript 2015,这意味着代码应该可以在支持 ECMAScript 2015 的环境中运行。因此,运行 tsc --target es2015 hello.ts 会得到以下输出:
🌐 By default TypeScript targets ES5, 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:
jsfunction greet(person, date) {console.log(`Hello ${person}, today is ${date.toDateString()}!`);}greet("Maddison", new Date());
虽然默认目标是 ES5,但绝大多数当前浏览器都支持 ES2015。 因此,大多数开发者可以安全地将目标指定为 ES2015 或更高版本,除非需要兼容某些古老的浏览器。
严格性
🌐 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 有几个可以开启或关闭的类型检查严格性标志,除非另有说明,否则我们所有的示例都会在启用所有标志的情况下编写。
在命令行中的 strict 标志,或者在 tsconfig.json 中的 "strict": true,可以同时开启它们,但我们也可以单独选择关闭。
你应该了解的两个最大标志是 noImplicitAny 和 strictNullChecks。
🌐 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
默认情况下,像 null 和 undefined 这样的值可以赋给任何其他类型。这可以让编写某些代码更容易,但忘记处理 null 和 undefined 是世界上无数错误的根源——有些人认为这是一个十亿美元的错误!strictNullChecks 标志使处理 null 和 undefined 更加明确,并且_避免_我们去担心是否_忘记_处理 null 和 undefined。
🌐 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.