TypeScript 3.8

仅类型导入和导出

¥Type-Only Imports and Export

大多数用户可能永远不需要考虑此功能;但是,如果你在 isolatedModules、TypeScript 的 transpileModule API 或 Babel 下遇到问题,则此功能可能适用。

¥This feature is something most users may never have to think about; however, if you’ve hit issues under isolatedModules, TypeScript’s transpileModule API, or Babel, this feature might be relevant.

TypeScript 3.8 为仅类型导入和导出添加了新的语法。

¥TypeScript 3.8 adds a new syntax for type-only imports and exports.

ts
import type { SomeThing } from "./some-module.js";
export type { SomeThing };

import type 仅导入用于类型注释和声明的声明。它始终会被完全删除,因此在运行时不会留下任何痕迹。同样,export type 仅提供可用于类型上下文的导出,并且也从 TypeScript 的输出中删除。

¥import type only imports declarations to be used for type annotations and declarations. It always gets fully erased, so there’s no remnant of it at runtime. Similarly, export type only provides an export that can be used for type contexts, and is also erased from TypeScript’s output.

需要注意的是,类在运行时具有值,在设计时具有类型,并且使用与上下文相关。使用 import type 导入类时,你无法执行从该类扩展等操作。

¥It’s important to note that classes have a value at runtime and a type at design-time, and the use is context-sensitive. When using import type to import a class, you can’t do things like extend from it.

ts
import type { Component } from "react";
interface ButtonProps {
// ...
}
class Button extends Component<ButtonProps> {
// ~~~~~~~~~
// error! 'Component' only refers to a type, but is being used as a value here.
// ...
}

如果你以前使用过 Flow,那么它们的语法非常相似。一个区别是,我们添加了一些限制,以避免出现可能出现歧义的代码。

¥If you’ve used Flow before, the syntax is fairly similar. One difference is that we’ve added a few restrictions to avoid code that might appear ambiguous.

ts
// Is only 'Foo' a type? Or every declaration in the import?
// We just give an error because it's not clear.
import type Foo, { Bar, Baz } from "some-module";
// ~~~~~~~~~~~~~~~~~~~~~~
// error! A type-only import can specify a default import or named bindings, but not both.

import type 结合使用时,TypeScript 3.8 还添加了一个新的编译器标志,用于控制运行时不会使用的导入的处理方式:importsNotUsedAsValues。此标志有 3 个不同的值:

¥In conjunction with import type, TypeScript 3.8 also adds a new compiler flag to control what happens with imports that won’t be utilized at runtime: importsNotUsedAsValues. This flag takes 3 different values:

  • remove:这是目前删除这些导入的行为。它将继续作为默认设置,并且是一项非破坏性更改。

    ¥remove: this is today’s behavior of dropping these imports. It’s going to continue to be the default, and is a non-breaking change.

  • preserve:这将保留所有值从未使用过的导入。这可能导致导入/副作用被保留。

    ¥preserve: this preserves all imports whose values are never used. This can cause imports/side-effects to be preserved.

  • error:这会保留所有导入(与 preserve 选项相同),但当值导入仅用作类型时会出错。如果你想确保没有意外导入任何值,但仍使副作用导入明确,这可能会很有用。

    ¥error: this preserves all imports (the same as the preserve option), but will error when a value import is only used as a type. This might be useful if you want to ensure no values are being accidentally imported, but still make side-effect imports explicit.

有关该功能的更多信息,请参阅 查看拉取请求相关变更,了解如何扩展从 import type 声明导入的范围。

¥For more information about the feature, you can take a look at the pull request, and relevant changes around broadening where imports from an import type declaration can be used.

ECMAScript 私有字段

¥ECMAScript Private Fields

TypeScript 3.8 增加了对 ECMAScript 私有字段(第三阶段类字段提案 的一部分)的支持。

¥TypeScript 3.8 brings support for ECMAScript’s private fields, part of the stage-3 class fields proposal.

ts
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
greet() {
console.log(`Hello, my name is ${this.#name}!`);
}
}
let jeremy = new Person("Jeremy Bearimy");
jeremy.#name;
// ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.

与常规属性(即使是使用 private 修饰符声明的属性)不同,私有字段需要牢记一些规则。其中一些代码如下:

¥Unlike regular properties (even ones declared with the private modifier), private fields have a few rules to keep in mind. Some of them are:

  • 私有字段以 # 字符开头。有时我们称之为私有名称。

    ¥Private fields start with a # character. Sometimes we call these private names.

  • 每个私有字段名称的作用域都唯一限定于其包含类。

    ¥Every private field name is uniquely scoped to its containing class.

  • TypeScript 可访问性修饰符(例如 publicprivate)不能用于私有字段。

    ¥TypeScript accessibility modifiers like public or private can’t be used on private fields.

  • 私有字段无法在包含类的外部访问甚至检测。 - 甚至 JS 用户也支持!有时我们称之为硬隐私。

    ¥Private fields can’t be accessed or even detected outside of the containing class - even by JS users! Sometimes we call this hard privacy.

除了 “hard” 的隐私性之外,私有字段的另一个好处就是我们刚才提到的唯一性。例如,常规属性声明很容易在子类中被覆盖。

¥Apart from “hard” privacy, another benefit of private fields is that uniqueness we just mentioned. For example, regular property declarations are prone to being overwritten in subclasses.

ts
class C {
foo = 10;
cHelper() {
return this.foo;
}
}
class D extends C {
foo = 20;
dHelper() {
return this.foo;
}
}
let instance = new D();
// 'this.foo' refers to the same property on each instance.
console.log(instance.cHelper()); // prints '20'
console.log(instance.dHelper()); // prints '20'

使用私有字段,你永远不必担心这个问题,因为每个字段名称对于包含的类都是唯一的。

¥With private fields, you’ll never have to worry about this, since each field name is unique to the containing class.

ts
class C {
#foo = 10;
cHelper() {
return this.#foo;
}
}
class D extends C {
#foo = 20;
dHelper() {
return this.#foo;
}
}
let instance = new D();
// 'this.#foo' refers to a different field within each class.
console.log(instance.cHelper()); // prints '10'
console.log(instance.dHelper()); // prints '20'

另一件值得注意的事情是,访问任何其他类型的私有字段都会导致 TypeError

¥Another thing worth noting is that accessing a private field on any other type will result in a TypeError!

ts
class Square {
#sideLength: number;
constructor(sideLength: number) {
this.#sideLength = sideLength;
}
equals(other: any) {
return this.#sideLength === other.#sideLength;
}
}
const a = new Square(100);
const b = { sideLength: 100 };
// Boom!
// TypeError: attempted to get private field on non-instance
// This fails because 'b' is not an instance of 'Square'.
console.log(a.equals(b));

最后,对于任何普通的 .js 文件用户,私有字段在赋值之前必须先声明。

¥Finally, for any plain .js file users, private fields always have to be declared before they’re assigned to.

js
class C {
// No declaration for '#foo'
// :(
constructor(foo: number) {
// SyntaxError!
// '#foo' needs to be declared before writing to it.
this.#foo = foo;
}
}

JavaScript 始终允许用户访问未声明的属性,而 TypeScript 始终要求类属性必须声明。对于私有字段,无论我们是在 .js 还是 .ts 文件中工作,始终都需要声明。

¥JavaScript has always allowed users to access undeclared properties, whereas TypeScript has always required declarations for class properties. With private fields, declarations are always needed regardless of whether we’re working in .js or .ts files.

js
class C {
/** @type {number} */
#foo;
constructor(foo: number) {
// This works.
this.#foo = foo;
}
}

有关实现的更多信息,请参阅 查看原始拉取请求

¥For more information about the implementation, you can check out the original pull request

我应该使用哪种类型?

¥Which should I use?

我们已经收到许多关于 TypeScript 用户应该使用哪种类型的私有变量的问题:最常见的问题是,“我应该使用 private 关键字,还是 ECMAScript 的哈希/磅符号 (#) 私有字段?” 这要视情况而定!

¥We’ve already received many questions on which type of privates you should use as a TypeScript user: most commonly, “should I use the private keyword, or ECMAScript’s hash/pound (#) private fields?” It depends!

对于属性,TypeScript 的 private 修饰符已被完全删除。 - 这意味着在运行时,它的行为完全像一个普通属性,并且无法判断它是用 private 修饰符声明的。使用 private 关键字时,隐私仅在编译时/设计时强制执行,对于 JavaScript 用户来说,它完全基于意图。

¥When it comes to properties, TypeScript’s private modifiers are fully erased - that means that at runtime, it acts entirely like a normal property and there’s no way to tell that it was declared with a private modifier. When using the private keyword, privacy is only enforced at compile-time/design-time, and for JavaScript consumers it’s entirely intent-based.

ts
class C {
private foo = 10;
}
// This is an error at compile time,
// but when TypeScript outputs .js files,
// it'll run fine and print '10'.
console.log(new C().foo); // prints '10'
// ~~~
// error! Property 'foo' is private and only accessible within class 'C'.
// TypeScript allows this at compile-time
// as a "work-around" to avoid the error.
console.log(new C()["foo"]); // prints '10'

优点是,这种 “软隐私” 可以帮助你的消费者暂时解决无法访问某些 API 的问题,并且在任何运行时都能正常工作。

¥The upside is that this sort of “soft privacy” can help your consumers temporarily work around not having access to some API, and also works in any runtime.

另一方面,ECMAScript 的 # 私有变量在类之外完全无法访问。

¥On the other hand, ECMAScript’s # privates are completely inaccessible outside of the class.

ts
class C {
#foo = 10;
}
console.log(new C().#foo); // SyntaxError
// ~~~~
// TypeScript reports an error *and*
// this won't work at runtime!
console.log(new C()["#foo"]); // prints undefined
// ~~~~~~~~~~~~~~~
// TypeScript reports an error under 'noImplicitAny',
// and this prints 'undefined'.

这种严格的隐私性对于严格确保没有人能够利用你的任何内部函数非常有用。如果你是库作者,删除或重命名私有字段绝不会造成重大变更。

¥This hard privacy is really useful for strictly ensuring that nobody can take use of any of your internals. If you’re a library author, removing or renaming a private field should never cause a breaking change.

正如我们提到的,另一个好处是,使用 ECMAScript 的 # 私有方法可以更容易地进行子类化,因为它们确实是私有的。使用 ECMAScript # 私有字段时,任何子类都无需担心字段命名冲突。对于 TypeScript 的 private 属性声明,用户仍然需要小心,不要破坏超类中声明的属性。

¥As we mentioned, another benefit is that subclassing can be easier with ECMAScript’s # privates because they really are private. When using ECMAScript # private fields, no subclass ever has to worry about collisions in field naming. When it comes to TypeScript’s private property declarations, users still have to be careful not to trample over properties declared in superclasses.

还有一件事需要考虑,那就是你打算在哪里运行代码。TypeScript 目前不支持此功能,除非针对 ECMAScript 2015 (ES6) 或更高版本的目标平台。这是因为我们的降级实现使用 WeakMap 来强制隐私,而 WeakMap 无法以不会导致内存泄漏的方式进行 polyfill。相比之下,TypeScript 的 private 声明的属性适用于所有目标。 - 甚至 ECMAScript 3!

¥One more thing to think about is where you intend for your code to run. TypeScript currently can’t support this feature unless targeting ECMAScript 2015 (ES6) targets or higher. This is because our downleveled implementation uses WeakMaps to enforce privacy, and WeakMaps can’t be polyfilled in a way that doesn’t cause memory leaks. In contrast, TypeScript’s private-declared properties work with all targets - even ECMAScript 3!

最后需要考虑的可能是速度:private 属性与其他属性没有区别,因此无论你针对哪个运行时,访问它们的速度都与访问其他属性一样快。由于 # 私有字段使用 WeakMap 进行了降级,因此使用起来可能会更慢。虽然某些运行时可能会优化其 # 私有字段的实际实现,甚至拥有快速的 WeakMap 实现,但并非所有运行时都是如此。

¥A final consideration might be speed: private properties are no different from any other property, so accessing them is as fast as any other property access no matter which runtime you target. In contrast, because # private fields are downleveled using WeakMaps, they may be slower to use. While some runtimes might optimize their actual implementations of # private fields, and even have speedy WeakMap implementations, that might not be the case in all runtimes.

export * as ns 语法

¥export * as ns Syntax

通常有一个入口点将另一个模块的所有成员作为单个成员公开。

¥It’s often common to have a single entry-point that exposes all the members of another module as a single member.

ts
import * as utilities from "./utilities.js";
export { utilities };

这非常常见,以至于 ECMAScript 2020 最近添加了新的语法来支持这种模式!

¥This is so common that ECMAScript 2020 recently added a new syntax to support this pattern!

ts
export * as utilities from "./utilities.js";

这是 JavaScript 的一项很好的用户体验改进,TypeScript 3.8 实现了此语法。当你的模块目标早于 es2020 时,TypeScript 将输出类似于第一个代码片段的内容。

¥This is a nice quality-of-life improvement to JavaScript, and TypeScript 3.8 implements this syntax. When your module target is earlier than es2020, TypeScript will output something along the lines of the first code snippet.

顶层 await

¥Top-Level await

TypeScript 3.8 支持即将推出的 ECMAScript 实用功能“顶层 await”。

¥TypeScript 3.8 provides support for a handy upcoming ECMAScript feature called “top-level await“.

JavaScript 用户通常为了使用 await 而引入一个 async 函数,然后在定义该函数后立即调用它。

¥JavaScript users often introduce an async function in order to use await, and then immediately called the function after defining it.

js
async function main() {
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);
}
main().catch((e) => console.error(e));

这是因为之前在 JavaScript(以及大多数具有类似功能的其他语言)中,await 仅允许在 async 函数的主体内使用。但是,有了顶层 await,我们就可以在模块的顶层使用 await

¥This is because previously in JavaScript (along with most other languages with a similar feature), await was only allowed within the body of an async function. However, with top-level await, we can use await at the top level of a module.

ts
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);
// Make sure we're a module
export {};

注意这里有一个微妙之处:顶层 await 仅在模块的顶层起作用,并且只有当 TypeScript 找到 importexport 时,文件才会被视为模块。在某些基本情况下,你可能需要将 export {} 写成一些样板文件来确保这一点。

¥Note there’s a subtlety: top-level await only works at the top level of a module, and files are only considered modules when TypeScript finds an import or an export. In some basic cases, you might need to write out export {} as some boilerplate to make sure of this.

顶层 await 可能无法在你目前预期的所有环境中正常工作。目前,只有当 target 编译器选项为 es2017 或更高版本,且 moduleesnextsystem 时,才能使用顶层 await。在多个环境和打包器中的支持可能有限,或者可能需要启用实验性支持。

¥Top level await may not work in all environments where you might expect at this point. Currently, you can only use top level await when the target compiler option is es2017 or above, and module is esnext or system. Support within several environments and bundlers may be limited or may require enabling experimental support.

有关我们实现的更多信息,请参阅 查看原始拉取请求

¥For more information on our implementation, you can check out the original pull request.

targetmodule 中的 es2020

¥es2020 for target and module

TypeScript 3.8 支持将 es2020 作为 moduletarget 的一个选项。这将保留较新的 ECMAScript 2020 功能,例如可选链、空值合并、export * as ns 和动态 import(...) 语法。这也意味着 bigint 字面量现在在 esnext 之下有一个稳定的 target

¥TypeScript 3.8 supports es2020 as an option for module and target. This will preserve newer ECMAScript 2020 features like optional chaining, nullish coalescing, export * as ns, and dynamic import(...) syntax. It also means bigint literals now have a stable target below esnext.

JSDoc 属性修饰符

¥JSDoc Property Modifiers

TypeScript 3.8 通过启用 allowJs 标志来支持 JavaScript 文件,并且还支持通过 checkJs 选项或在 .js 文件顶部添加 // @ts-check 注释来对这些 JavaScript 文件进行类型检查。

¥TypeScript 3.8 supports JavaScript files by turning on the allowJs flag, and also supports type-checking those JavaScript files via the checkJs option or by adding a // @ts-check comment to the top of your .js files.

由于 JavaScript 文件没有专门的类型检查语法,因此 TypeScript 利用了 JSDoc。TypeScript 3.8 可以识别一些新的属性 JSDoc 标签。

¥Because JavaScript files don’t have dedicated syntax for type-checking, TypeScript leverages JSDoc. TypeScript 3.8 understands a few new JSDoc tags for properties.

首先是可访问性修饰符:@public@private@protected。这些标签的工作方式与 TypeScript 中的 publicprivateprotected 完全相同。

¥First are the accessibility modifiers: @public, @private, and @protected. These tags work exactly like public, private, and protected respectively work in TypeScript.

js
// @ts-check
class Foo {
constructor() {
/** @private */
this.stuff = 100;
}
printStuff() {
console.log(this.stuff);
}
}
new Foo().stuff;
// ~~~~~
// error! Property 'stuff' is private and only accessible within class 'Foo'.
  • @public 总是隐含的,可以省略,但表示可以从任何地方访问属性。

    ¥@public is always implied and can be left off, but means that a property can be reached from anywhere.

  • @private 表示属性只能在包含类中使用。

    ¥@private means that a property can only be used within the containing class.

  • @protected 表示属性只能在包含类和所有派生子类中使用,但不能在包含类的不同实例上使用。

    ¥@protected means that a property can only be used within the containing class, and all derived subclasses, but not on dissimilar instances of the containing class.

接下来,我们还添加了 @readonly 修饰符,以确保属性仅在初始化期间被写入。

¥Next, we’ve also added the @readonly modifier to ensure that a property is only ever written to during initialization.

js
// @ts-check
class Foo {
constructor() {
/** @readonly */
this.stuff = 100;
}
writeToStuff() {
this.stuff = 200;
// ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.
}
}
new Foo().stuff++;
// ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.

在 Linux 和 Linux 上更好的目录监视 watchOptions

¥Better Directory Watching on Linux and watchOptions

TypeScript 3.8 提供了一种新的目录监视策略,这对于高效地获取 node_modules 的更改至关重要。

¥TypeScript 3.8 ships a new strategy for watching directories, which is crucial for efficiently picking up changes to node_modules.

在某些情况下,在 Linux 等操作系统上,TypeScript 会在 node_modules 及其许多子目录中安装目录监视程序(而不是文件监视程序),以检测依赖的变化。这是因为可用的文件监视器的数量通常会被 node_modules 中的文件数量所掩盖,而要跟踪的目录要少得多。

¥For some context, on operating systems like Linux, TypeScript installs directory watchers (as opposed to file watchers) on node_modules and many of its subdirectories to detect changes in dependencies. This is because the number of available file watchers is often eclipsed by the number of files in node_modules, whereas there are way fewer directories to track.

旧版本的 TypeScript 会立即在文件夹上安装目录监视程序,并且在启动时没有问题;但是,在 npm 安装期间,node_modules 中会发生很多活动,这可能会使 TypeScript 不堪重负,经常使编辑器会话变得非常缓慢。为了防止这种情况,TypeScript 3.8 在安装目录监视程序之前会稍等片刻,以便让这些高度不稳定的目录有时间稳定下来。

¥Older versions of TypeScript would immediately install directory watchers on folders, and at startup that would be fine; however, during an npm install, a lot of activity will take place within node_modules and that can overwhelm TypeScript, often slowing editor sessions to a crawl. To prevent this, TypeScript 3.8 waits slightly before installing directory watchers to give these highly volatile directories some time to stabilize.

由于每个项目可能在不同的策略下运行得更好,并且这种新方法可能不适合你工作流程,TypeScript 3.8 在 tsconfig.jsonjsconfig.json 中引入了一个新的 watchOptions 字段,允许用户告诉编译器/语言服务应该使用哪些监视策略来跟踪文件和目录。

¥Because every project might work better under different strategies, and this new approach might not work well for your workflows, TypeScript 3.8 introduces a new watchOptions field in tsconfig.json and jsconfig.json which allows users to tell the compiler/language service which watching strategies should be used to keep track of files and directories.

{
// Some typical compiler options
"": "es2020",
"": "node"
// ...
},
// NEW: Options for file/directory watching
"watchOptions": {
// Use native file system events for files and directories
"": "useFsEvents",
"": "useFsEvents",
// Poll files for updates more frequently
// when they're updated a lot.
"": "dynamicPriority"
}
}

watchOptions 包含 4 个可配置的新选项:

¥watchOptions contains 4 new options that can be configured:

  • watchFile:监视单个文件的策略。可以设置为

    ¥watchFile: the strategy for how individual files are watched. This can be set to

    • fixedPollingInterval:以固定间隔每秒检查每个文件是否有更改。

      ¥fixedPollingInterval: Check every file for changes several times a second at a fixed interval.

    • priorityPollingInterval:每秒检查每个文件是否有更改,但使用启发式方法检查某些类型的文件的频率要低于其他文件。

      ¥priorityPollingInterval: Check every file for changes several times a second, but use heuristics to check certain types of files less frequently than others.

    • dynamicPriorityPolling:使用动态队列,其中修改频率较低的文件将较少被检查。

      ¥dynamicPriorityPolling: Use a dynamic queue where less-frequently modified files will be checked less often.

    • useFsEvents(默认值):尝试使用操作系统/文件系统的原生事件进行文件更改。

      ¥useFsEvents (the default): Attempt to use the operating system/file system’s native events for file changes.

    • useFsEventsOnParentDirectory:尝试使用操作系统/文件系统的原生事件来监听文件所在目录的更改。这可以使用更少的文件监视器,但准确性可能较低。

      ¥useFsEventsOnParentDirectory: Attempt to use the operating system/file system’s native events to listen for changes on a file’s containing directories. This can use fewer file watchers, but might be less accurate.

  • watchDirectory:在缺乏递归文件监视功能的系统下,监视整个目录树的策略。可以设置为:

    ¥watchDirectory: the strategy for how entire directory trees are watched under systems that lack recursive file-watching functionality. This can be set to:

    • fixedPollingInterval:以固定间隔每秒检查每个目录是否有更改。

      ¥fixedPollingInterval: Check every directory for changes several times a second at a fixed interval.

    • dynamicPriorityPolling:使用动态队列,其中修改频率较低的目录将较少被检查。

      ¥dynamicPriorityPolling: Use a dynamic queue where less-frequently modified directories will be checked less often.

    • useFsEvents(默认值):尝试使用操作系统/文件系统的原生事件进行目录更改。

      ¥useFsEvents (the default): Attempt to use the operating system/file system’s native events for directory changes.

  • fallbackPolling:使用文件系统事件时,此选项指定当系统用完原生文件监视器和/或不支持原生文件监视器时使用的轮询策略。可以设置为

    ¥fallbackPolling: when using file system events, this option specifies the polling strategy that gets used when the system runs out of native file watchers and/or doesn’t support native file watchers. This can be set to

    • fixedPollingInterval:(见上文)

      ¥fixedPollingInterval: (See above.)

    • priorityPollingInterval:(见上文)

      ¥priorityPollingInterval: (See above.)

    • dynamicPriorityPolling:(见上文)

      ¥dynamicPriorityPolling: (See above.)

    • synchronousWatchDirectory:禁用目录上的延迟监视。当可能同时发生大量文件更改时,延迟监视很有用(例如,node_modules 中的更改来自运行 npm install),但你可能希望使用此标志禁用它以进行一些不太常见的设置。

      ¥synchronousWatchDirectory: Disable deferred watching on directories. Deferred watching is useful when lots of file changes might occur at once (e.g. a change in node_modules from running npm install), but you might want to disable it with this flag for some less-common setups.

有关这些更改的更多信息,请参阅 前往 GitHub 查看拉取请求 以了解更多信息。

¥For more information on these changes, head over to GitHub to see the pull request to read more.

“快速而松散” 增量检查

¥“Fast and Loose” Incremental Checking

TypeScript 3.8 引入了一个名为 assumeChangesOnlyAffectDirectDependencies 的新编译器选项。启用此选项后,TypeScript 将避免重新检查/重建所有真正可能受影响的文件,而仅重新检查/重建已更改的文件以及直接导入它们的文件。

¥TypeScript 3.8 introduces a new compiler option called assumeChangesOnlyAffectDirectDependencies. When this option is enabled, TypeScript will avoid rechecking/rebuilding all truly possibly-affected files, and only recheck/rebuild files that have changed as well as files that directly import them.

例如,考虑一个文件 fileD.ts,它导入了 fileC.tsfileC.ts 导入了 fileB.tsfileB.ts 导入了 fileA.ts,如下所示:

¥For example, consider a file fileD.ts that imports fileC.ts that imports fileB.ts that imports fileA.ts as follows:

fileA.ts <- fileB.ts <- fileC.ts <- fileD.ts

--watch 模式下,fileA.ts 的更改通常意味着 TypeScript 至少需要重新检查 fileB.tsfileC.tsfileD.ts。在 assumeChangesOnlyAffectDirectDependencies 下,fileA.ts 的更改意味着只需要重新检查 fileA.tsfileB.ts

¥In --watch mode, a change in fileA.ts would typically mean that TypeScript would need to at least re-check fileB.ts, fileC.ts, and fileD.ts. Under assumeChangesOnlyAffectDirectDependencies, a change in fileA.ts means that only fileA.ts and fileB.ts need to be re-checked.

在像 Visual Studio Code 这样的代码库中,这将某些文件更改的重建时间从大约 14 秒缩短到大约 1 秒。虽然我们不一定建议所有代码库都使用此选项,但如果你拥有非常庞大的代码库,并且愿意将完整的项目错误推迟到以后(例如,通过 tsconfig.fullbuild.json 或在 CI 中进行专门的构建),你可能会对此感兴趣。

¥In a codebase like Visual Studio Code, this reduced rebuild times for changes in certain files from about 14 seconds to about 1 second. While we don’t necessarily recommend this option for all codebases, you might be interested if you have an extremely large codebase and are willing to defer full project errors until later (e.g. a dedicated build via a tsconfig.fullbuild.json or in CI).

更多详情,请参阅 查看原始拉取请求

¥For more details, you can see the original pull request.