TypeScript 4.5

支持来自 node_moduleslib

🌐 Supporting lib from node_modules

为了确保 TypeScript 和 JavaScript 的支持能够开箱即用,TypeScript 打包了一系列声明文件(.d.ts 文件)。这些声明文件表示 JavaScript 语言中可用的 API,以及标准浏览器 DOM API。虽然根据你的 target 有一些合理的默认值,但你可以通过在 tsconfig.json 中配置 lib 设置来选择你的程序使用哪些声明文件。

🌐 To ensure that TypeScript and JavaScript support works well out of the box, TypeScript bundles a series of declaration files (.d.ts files). These declaration files represent the available APIs in the JavaScript language, and the standard browser DOM APIs. While there are some reasonable defaults based on your target, you can pick and choose which declaration files your program uses by configuring the lib setting in the tsconfig.json.

不过,在 TypeScript 中包含这些声明文件偶尔会有两个缺点:

🌐 There are two occasional downsides to including these declaration files with TypeScript though:

  • 升级 TypeScript 时,你还必须处理 TypeScript 内置声明文件的更改,而当 DOM API 频繁更改时,这可能是一个挑战。
  • 很难根据项目依赖的需求定制这些文件(例如,如果你的依赖声明使用 DOM API,你可能也会被迫使用 DOM API)。

TypeScript 4.5 引入了一种覆盖特定内置 lib 的方法,其方式类似于 @types/ 支持的工作方式。决定 TypeScript 应该包含哪些 lib 文件时,它会首先在 node_modules 中查找范围限定的 @typescript/lib-* 包。例如,在将 dom 作为 lib 中的一个选项包含时,TypeScript 将使用 node_modules/@typescript/lib-dom 中的类型(如果可用)。

🌐 TypeScript 4.5 introduces a way to override a specific built-in lib in a manner similar to how @types/ support works. When deciding which lib files TypeScript should include, it will first look for a scoped @typescript/lib-* package in node_modules. For example, when including dom as an option in lib, TypeScript will use the types in node_modules/@typescript/lib-dom if available.

然后你可以使用你的包管理器安装特定的包,以替代给定的 lib。 例如,今天 TypeScript 在 @types/web 上发布了 DOM API 的版本。 如果你想将你的项目锁定到特定版本的 DOM API,你可以将此添加到你的 package.json

🌐 You can then use your package manager to install a specific package to take over for a given lib For example, today TypeScript publishes versions of the DOM APIs on @types/web. If you wanted to lock your project to a specific version of the DOM APIs, you could add this to your package.json:

json
{
"dependencies": {
"@typescript/lib-dom": "npm:@types/web"
}
}

从 4.5 开始,你可以更新 TypeScript,并且你的依赖管理器的锁文件将确保使用完全相同版本的 DOM 类型。这意味着你可以按自己的节奏更新类型。

🌐 Then from 4.5 onwards, you can update TypeScript and your dependency manager’s lockfile will ensure that it uses the exact same version of the DOM types. That means you get to update your types on your own terms.

我们想向 saschanaz 致以感谢,他在我们开发和尝试这个功能的过程中表现出了极大的帮助和耐心。

🌐 We’d like to give a shout-out to saschanaz who has been extremely helpful and patient as we’ve been building out and experimenting with this feature.

欲了解更多信息,你可以查看此更改的实现

🌐 For more information, you can see the implementation of this change.

Awaited 类型与 Promise 改进

🌐 The Awaited Type and Promise Improvements

TypeScript 4.5 引入了一个名为 Awaited 的新工具类型。这个类型用于模拟类似于 async 函数中的 await 操作,或 Promise 上的 .then() 方法——特别是它们递归解包 Promise 的方式。

🌐 TypeScript 4.5 introduces a new utility type called the Awaited type. This type is meant to model operations like await in async functions, or the .then() method on Promises - specifically, the way that they recursively unwrap Promises.

ts
// A = string
type A = Awaited<Promise<string>>;
// B = number
type B = Awaited<Promise<Promise<number>>>;
// C = boolean | number
type C = Awaited<boolean | Promise<number>>;

Awaited 类型在建模现有 API 时非常有用,包括 JavaScript 内置的 Promise.allPromise.race 等。实际上,围绕 Promise.all 推断的一些问题促成了 Awaited 的出现。这里有一个在 TypeScript 4.4 及更早版本中会失败的示例。

🌐 The Awaited type can be helpful for modeling existing APIs, including JavaScript built-ins like Promise.all, Promise.race, etc. In fact, some of the problems around inference with Promise.all served as motivations for Awaited. Here’s an example that fails in TypeScript 4.4 and earlier.

ts
declare function MaybePromise<T>(value: T): T | Promise<T> | PromiseLike<T>;
async function doSomething(): Promise<[number, number]> {
const result = await Promise.all([MaybePromise(100), MaybePromise(200)]);
// Error!
//
// [number | Promise<100>, number | Promise<200>]
//
// is not assignable to type
//
// [number, number]
return result;
}

现在,Promise.all 利用某些特性与 Awaited 的结合来提供更好的推断结果,上面的例子也可以正常工作。

🌐 Now Promise.all leverages the combination of certain features with Awaited to give much better inference results, and the above example works.

有关更多信息,你可以在 GitHub 上阅读有关此更改的内容

🌐 For more information, you can read about this change on GitHub.

模板字符串类型作为判别式

🌐 Template String Types as Discriminants

TypeScript 4.5 现在可以缩小具有模板字符串类型的值的范围,并且还可以将模板字符串类型识别为判别式。

🌐 TypeScript 4.5 now can narrow values that have template string types, and also recognizes template string types as discriminants.

例如,下面的代码片段过去会失败,但现在在 TypeScript 4.5 中可以成功进行类型检查。

🌐 As an example, the following used to fail, but now successfully type-checks in TypeScript 4.5.

ts
export interface Success {
type: `${string}Success`;
body: string;
}
 
export interface Error {
type: `${string}Error`;
message: string
}
 
export function handler(r: Success | Error) {
if (r.type === "HttpSuccess") {
const token = r.body;
(parameter) r: Success
}
}
Try

欲了解更多信息,请参阅启用此功能的更改

🌐 For more information, see the change that enables this feature.

module es2022

多亏了 Kagami S. Rosylight,TypeScript 现在支持一个新的 module 设置:es2022module es2022 中的主要特性是顶层 await,这意味着你可以在 async 函数之外使用 await。 这在 --module esnext(现在的 --module nodenext)中已经支持,但 es2022 是这个特性的第一个稳定目标。

🌐 Thanks to Kagami S. Rosylight, TypeScript now supports a new module setting: es2022. The main feature in module es2022 is top-level await, meaning you can use await outside of async functions. This was already supported in --module esnext (and now --module nodenext), but es2022 is the first stable target for this feature.

你可以在这里了解有关此更改的更多信息

🌐 You can read up more on this change here.

条件类型的尾递归消除

🌐 Tail-Recursion Elimination on Conditional Types

当 TypeScript 检测到可能的无限递归或任何可能耗时且影响编辑器体验的类型展开时,它通常需要优雅地失败。因此,TypeScript 采用了一些启发式方法,以确保在尝试解析无限深度类型或处理会生成大量中间结果的类型时不会出现问题。

🌐 TypeScript often needs to gracefully fail when it detects possibly infinite recursion, or any type expansions that can take a long time and affect your editor experience. As a result, TypeScript has heuristics to make sure it doesn’t go off the rails when trying to pick apart an infinitely-deep type, or working with types that generate a lot of intermediate results.

ts
type InfiniteBox<T> = { item: InfiniteBox<T> };
type Unpack<T> = T extends { item: infer U } ? Unpack<U> : T;
// error: Type instantiation is excessively deep and possibly infinite.
type Test = Unpack<InfiniteBox<number>>;

以上示例故意设计得简单且无用,但实际上有很多类型是有用的,不幸的是,它们会触发我们的启发式算法。 例如,下面的 TrimLeft 类型会删除字符串类型开头的空格。 如果给定的字符串类型开头有空格,它会立即将字符串的其余部分重新输入到 TrimLeft 中。

🌐 The above example is intentionally simple and useless, but there are plenty of types that are actually useful, and unfortunately trigger our heuristics. As an example, the following TrimLeft type removes spaces from the beginning of a string-like type. If given a string type that has a space at the beginning, it immediately feeds the remainder of the string back into TrimLeft.

ts
type TrimLeft<T extends string> =
T extends ` ${infer Rest}` ? TrimLeft<Rest> : T;
// Test = "hello" | "world"
type Test = TrimLeft<" hello" | " world">;

此类型很有用,但如果字符串的前导空格数为 50,则会出错。

🌐 This type can be useful, but if a string has 50 leading spaces, you’ll get an error.

ts
type TrimLeft<T extends string> =
T extends ` ${infer Rest}` ? TrimLeft<Rest> : T;
// error: Type instantiation is excessively deep and possibly infinite.
type Test = TrimLeft<" oops">;

这很不幸,因为这种类型在对字符串进行操作建模时通常非常有用——例如,用于 URL 路由的解析器。更糟糕的是,一个更有用的类型通常会创建更多的类型实例,从而对输入长度有更多的限制。

🌐 That’s unfortunate, because these kinds of types tend to be extremely useful in modeling operations on strings - for example, parsers for URL routers. To make matters worse, a more useful type typically creates more type instantiations, and in turn has even more limitations on input length.

但有一个救赎之处:TrimLeft 的编写方式在某个分支中是_尾递归_的。当它再次调用自身时,会立即返回结果,并且不会对结果进行任何操作。由于这些类型不需要创建任何中间结果,因此它们可以更快地实现,并且可以避免触发 TypeScript 内置的许多类型递归启发式规则。

🌐 But there’s a saving grace: TrimLeft is written in a way that is tail-recursive in one branch. When it calls itself again, it immediately returns the result and doesn’t do anything with it. Because these types don’t need to create any intermediate results, they can be implemented more quickly and in a way that avoids triggering many of type recursion heuristics that are built into TypeScript.

这就是为什么 TypeScript 4.5 会对条件类型执行某些尾递归消除的原因。只要条件类型的一个分支仅仅是另一个条件类型,TypeScript 就可以避免中间实例化。仍然有一些启发式方法来确保这些类型不会失控,但它们要宽松得多。

🌐 That’s why TypeScript 4.5 performs some tail-recursion elimination on conditional types. As long as one branch of a conditional type is simply another conditional type, TypeScript can avoid intermediate instantiations. There are still heuristics to ensure that these types don’t go off the rails, but they are much more generous.

请记住,以下类型不会被优化,因为它通过将条件类型的结果添加到联合类型中来使用。

🌐 Keep in mind, the following type won’t be optimized, since it uses the result of a conditional type by adding it to a union.

ts
type GetChars<S> =
S extends `${infer Char}${infer Rest}` ? Char | GetChars<Rest> : never;

如果你想将其改成尾递归,你可以引入一个带有“累加器”类型参数的辅助函数,就像尾递归函数一样。

🌐 If you would like to make it tail-recursive, you can introduce a helper that takes an “accumulator” type parameter, just like with tail-recursive functions.

ts
type GetChars<S> = GetCharsHelper<S, never>;
type GetCharsHelper<S, Acc> =
S extends `${infer Char}${infer Rest}` ? GetCharsHelper<Rest, Char | Acc> : Acc;

你可以在这里阅读更多关于实现的内容 这里

🌐 You can read up more on the implementation here.

禁用导入省略

🌐 Disabling Import Elision

在某些情况下,TypeScript 无法检测到你正在使用某个导入。例如,看看以下代码:

🌐 There are some cases where TypeScript can’t detect that you’re using an import. For example, take the following code:

ts
import { Animal } from "./animal.js";
eval("console.log(new Animal().isDangerous())");

默认情况下,TypeScript 总是会移除这个导入,因为它看起来未被使用。在 TypeScript 4.5 中,你可以启用一个名为 preserveValueImports 的新标志,以防止 TypeScript 从你的 JavaScript 输出中删除任何导入的值。使用 eval 的合理理由很少,但类似的情况在 Svelte 中确实会发生:

🌐 By default, TypeScript always removes this import because it appears to be unused. In TypeScript 4.5, you can enable a new flag called preserveValueImports to prevent TypeScript from stripping out any imported values from your JavaScript outputs. Good reasons to use eval are few and far between, but something very similar to this happens in Svelte:

html
<!-- A .svelte File -->
<script>
import { someFunc } from "./some-module.js";
</script>
<button on:click="{someFunc}">Click me!</button>

在 Vue.js 中,同时使用它的 <script setup> 功能:

🌐 along with in Vue.js, using its <script setup> feature:

html
<!-- A .vue File -->
<script setup>
import { someFunc } from "./some-module.js";
</script>
<button @click="someFunc">Click me!</button>

这些框架会根据 <script> 标签之外的标记生成一些代码,但 TypeScript 能看到 <script> 标签内的代码。这意味着 TypeScript 会自动删除对 someFunc 的导入,上面的代码将无法运行!在 TypeScript 4.5 中,你可以使用 preserveValueImports 来避免这些情况。

🌐 These frameworks generate some code based on markup outside of their <script> tags, but TypeScript only sees code within the <script> tags. That means TypeScript will automatically drop the import of someFunc, and the above code won’t be runnable! With TypeScript 4.5, you can use preserveValueImports to avoid these situations.

请注意,当与 —isolatedModules` 结合使用时,这个标志有一个特殊要求:导入的类型必须标记为仅类型,因为一次处理单个文件的编译器无法判断导入的是未使用的值,还是为了避免运行时崩溃必须删除的类型。

🌐 Note that this flag has a special requirement when combined with —isolatedModules`: imported types must be marked as type-only because compilers that process single files at a time have no way of knowing whether imports are values that appear unused, or a type that must be removed in order to avoid a runtime crash.

ts
// Which of these is a value that should be preserved? tsc knows, but `ts.transpileModule`,
// ts-loader, esbuild, etc. don't, so `isolatedModules` gives an error.
import { someFunc, BaseType } from "./some-module.js";
// ^^^^^^^^
// Error: 'BaseType' is a type and must be imported using a type-only import
// when 'preserveValueImports' and 'isolatedModules' are both enabled.

这使得另一个 TypeScript 4.5 的功能,在导入名称上使用 type 修饰符,变得尤为重要。

🌐 That makes another TypeScript 4.5 feature, type modifiers on import names, especially important.

欲了解更多信息,请查看此拉取请求

🌐 For more information, see the pull request here.

type 导入名称上的修饰符

🌐 type Modifiers on Import Names

如上所述,preserveValueImportsisolatedModules 有特殊要求,以确保构建工具不会对删除类型导入是否安全产生歧义。

🌐 As mentioned above, preserveValueImports and isolatedModules have special requirements so that there’s no ambiguity for build tools whether it’s safe to drop type imports.

ts
// Which of these is a value that should be preserved? tsc knows, but `ts.transpileModule`,
// ts-loader, esbuild, etc. don't, so `isolatedModules` issues an error.
import { someFunc, BaseType } from "./some-module.js";
// ^^^^^^^^
// Error: 'BaseType' is a type and must be imported using a type-only import
// when 'preserveValueImports' and 'isolatedModules' are both enabled.

当这些选项结合使用时,我们需要一种方法来标示何时可以合法地省略导入。TypeScript 已经通过 import type 提供了相应的功能:

🌐 When these options are combined, we need a way to signal when an import can be legitimately dropped. TypeScript already has something for this with import type:

ts
import type { BaseType } from "./some-module.js";
import { someFunc } from "./some-module.js";
export class Thing implements BaseType {
// ...
}

这可以工作,但如果能避免对同一个模块写两条导入语句就好了。这也是 TypeScript 4.5 允许在单独的命名导入上使用 type 修饰符的部分原因,这样你就可以根据需要进行混合搭配。

🌐 This works, but it would be nice to avoid two import statements for the same module. That’s part of why TypeScript 4.5 allows a type modifier on individual named imports, so that you can mix and match as needed.

ts
import { someFunc, type BaseType } from "./some-module.js";
export class Thing implements BaseType {
someMethod() {
someFunc();
}
}

在上述示例中,BaseType 总是会被擦除,而 someFunc 会在 preserveValueImports 下被保留,最终得到如下代码:

🌐 In the above example, BaseType is always guaranteed to be erased and someFunc will be preserved under preserveValueImports, leaving us with the following code:

js
import { someFunc } from "./some-module.js";
export class Thing {
someMethod() {
someFunc();
}
}

欲了解更多信息,请参阅 GitHub 上的更改

🌐 For more information, see the changes on GitHub.

私有字段存在性检查

🌐 Private Field Presence Checks

TypeScript 4.5 支持一个 ECMAScript 提案,用于检查对象是否拥有私有字段。你现在可以编写一个带有 #private 字段成员的类,并使用 in 操作符查看另一个对象是否具有相同的字段。

🌐 TypeScript 4.5 supports an ECMAScript proposal for checking whether an object has a private field on it. You can now write a class with a #private field member and see whether another object has the same field by using the in operator.

ts
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
equals(other: unknown) {
return other &&
typeof other === "object" &&
#name in other && // <- this is new!
this.#name === other.#name;
}
}

这个功能的一个有趣方面是,检查 #name in other 意味着 other 必须已经被构造为 Person,因为没有其他方式可以使该字段存在。事实上,这正是该提案的关键特性之一,这也是该提案被命名为“符合人体工学的品牌检查”的原因——因为私有字段通常充当“品牌”,用于防护非该类实例的对象。因此,TypeScript 能够在每次检查时适当地缩小 other 的类型,直到最终得出 Person 类型。

🌐 One interesting aspect of this feature is that the check #name in other implies that other must have been constructed as a Person, since there’s no other way that field could be present. This is actually one of the key features of the proposal, and it’s why the proposal is named “ergonomic brand checks” - because private fields often act as a “brand” to guard against objects that aren’t instances of their class. As such, TypeScript is able to appropriately narrow the type of other on each check, until it ends up with the type Person.

我们要向彭博社的朋友们表示衷心的感谢,感谢他们贡献了这个拉取请求:Ashley ClaymoreTitian Cernicova-DragomirKubilay KahveciRob Palmer

🌐 We’d like to extend a big thanks to our friends at Bloomberg who contributed this pull request: Ashley Claymore, Titian Cernicova-Dragomir, Kubilay Kahveci, and Rob Palmer!

导入断言

🌐 Import Assertions

TypeScript 4.5 支持 ECMAScript 的 import assertions 提案。这是一种由运行时使用的语法,用于确保导入具有预期的格式。

🌐 TypeScript 4.5 supports an ECMAScript proposal for import assertions. This is a syntax used by runtimes to make sure that an import has an expected format.

ts
import obj from "./something.json" assert { type: "json" };

由于这些断言是特定于宿主的,因此 TypeScript 不会检查它们的内容,因此它们只是被保留下来,以便浏览器和运行时能够处理它们(并且可能出现错误)。

🌐 The contents of these assertions are not checked by TypeScript since they’re host-specific, and are simply left alone so that browsers and runtimes can handle them (and possibly error).

ts
// TypeScript is fine with this.
// But your browser? Probably not.
import obj from "./something.json" assert {
type: "fluffy bunny"
};

动态 import() 调用也可以通过第二个参数使用导入断言。

🌐 Dynamic import() calls can also use import assertions through a second argument.

ts
const obj = await import("./something.json", {
assert: { type: "json" },
});

第二个参数的预期类型由一个名为 ImportCallOptions 的新类型定义,目前只接受一个 assert 属性。

🌐 The expected type of that second argument is defined by a new type called ImportCallOptions, and currently only accepts an assert property.

我们想感谢 Wenlu Wang 实现了这个功能

🌐 We’d like to thank Wenlu Wang for implementing this feature!

JSDoc 中的常量断言和默认类型参数

🌐 Const Assertions and Default Type Arguments in JSDoc

TypeScript 4.5 为我们的 JSDoc 支持带来了一些额外的表达能力。

🌐 TypeScript 4.5 brings some extra expressivity to our JSDoc support.

一个例子是 const 断言。在 TypeScript 中,你可以通过在字面量后写 as const 来获得更精确且不可变的类型。

🌐 One example of this is with const assertions. In TypeScript, you can get a more precise and immutable type by writing as const after a literal.

ts
// type is { prop: string }
let a = { prop: "hello" };
// type is { readonly prop: "hello" }
let b = { prop: "hello" } as const;

在 JavaScript 文件中,现在可以使用 JSDoc 类型断言来实现同样的效果。

🌐 In JavaScript files, you can now use JSDoc type assertions to achieve the same thing.

ts
// type is { prop: string }
let a = { prop: "hello" };
// type is { readonly prop: "hello" }
let b = /** @type {const} */ ({ prop: "hello" });

提醒一下,JSDoc 类型断言注释以 /** @type {TheTypeWeWant} */ 开头,后面跟一个带括号的表达式:

🌐 As a reminder, JSDoc type assertions comments start with /** @type {TheTypeWeWant} */ and are followed by a parenthesized expression:

js
/** @type {TheTypeWeWant} */` (someExpression)

TypeScript 4.5 还为 JSDoc 添加了默认类型参数,这意味着 TypeScript 中的以下 type 声明:

🌐 TypeScript 4.5 also adds default type arguments to JSDoc, which means the following type declaration in TypeScript:

ts
type Foo<T extends string | number = number> = { prop: T };

可以重写为 JavaScript 中以下 @typedef 声明:

🌐 can be rewritten as the following @typedef declaration in JavaScript:

js
/**
* @template {string | number} [T=number]
* @typedef Foo
* @property prop {T}
*/
// or
/**
* @template {string | number} [T=number]
* @typedef {{ prop: T }} Foo
*/

有关更多信息,请参阅关于 const 声明的拉取请求以及类型参数默认值的更改

🌐 For more information, see the pull request for const assertions along with the changes for type argument defaults.

realPathSync.native 提升加载速度

🌐 Faster Load Time with realPathSync.native

TypeScript 现在在所有操作系统上都利用了 Node.js realPathSync 函数的系统本地实现。

🌐 TypeScript now leverages a system-native implementation of the Node.js realPathSync function on all operating systems.

以前这个功能只在 Linux 上使用,但在 TypeScript 4.5 中,它已被应用于通常不区分大小写的操作系统,如 Windows 和 MacOS。在某些代码库中,这一变化提高了项目加载速度5-13%(具体取决于宿主操作系统)。

🌐 Previously this function was only used on Linux, but in TypeScript 4.5 it has been adopted to operating systems that are typically case-insensitive, like Windows and MacOS. On certain codebases, this change sped up project loading by 5-13% (depending on the host operating system).

有关更多信息,请参阅此处的原始更改,以及此处针对4.5版本的更改

🌐 For more information, see the original change here, along with the 4.5-specific changes here.

JSX 属性的代码片段补全

🌐 Snippet Completions for JSX Attributes

TypeScript 4.5 为 JSX 属性带来了 片段补全 功能。当在 JSX 标签中输入属性时,TypeScript 已经会提供这些属性的建议;但是通过片段补全,它们可以通过添加初始化器并将光标放在正确的位置来减少一些额外的输入。

🌐 TypeScript 4.5 brings snippet completions for JSX attributes. When writing out an attribute in a JSX tag, TypeScript will already provide suggestions for those attributes; but with snippet completions, they can remove a little bit of extra typing by adding an initializer and putting your cursor in the right place.

Snippet completions for JSX attributes. For a string property, quotes are automatically added. For a numeric properties, braces are added.

TypeScript 通常使用属性的类型来确定要插入哪种初始化器,但你可以在 Visual Studio Code 中自定义此行为。

🌐 TypeScript will typically use the type of an attribute to figure out what kind of initializer to insert, but you can customize this behavior in Visual Studio Code.

Settings in VS Code for JSX attribute completions

请记住,此功能仅在较新版本的 Visual Studio Code 中可用,因此你可能需要使用 Insiders 版本才能使其工作。 更多信息,请查阅原始拉取请求

🌐 Keep in mind, this feature will only work in newer versions of Visual Studio Code, so you might have to use an Insiders build to get this working. For more information, read up on the original pull request

更好地处理未解析类型的编辑器支持

🌐 Better Editor Support for Unresolved Types

在某些情况下,编辑器会利用轻量级的“部分”语义模式——无论是在编辑器等待整个项目加载时,还是在像 GitHub 的基于网络的编辑器 这样的环境中。

🌐 In some cases, editors will leverage a lightweight “partial” semantic mode - either while the editor is waiting for the full project to load, or in contexts like GitHub’s web-based editor.

在较老版本的 TypeScript 中,如果语言服务找不到某个类型,它只会打印 any

🌐 In older versions of TypeScript, if the language service couldn’t find a type, it would just print any.

Hovering over a signature where Buffer isn't found, TypeScript replaces it with any.

在上面的例子中,Buffer 没有被找到,所以 TypeScript 在 快速信息 中将其替换为 any。 在 TypeScript 4.5 中,TypeScript 会尽力保留你写的内容。

🌐 In the above example, Buffer wasn’t found, so TypeScript replaced it with any in quick info. In TypeScript 4.5, TypeScript will try its best to preserve what you wrote.

Hovering over a signature where Buffer isn't found, it continues to use the name Buffer.

但是,如果你将鼠标悬停在 Buffer 上,你会看到一个提示,TypeScript 找不到 Buffer

🌐 However, if you hover over Buffer itself, you’ll get a hint that TypeScript couldn’t find Buffer.

TypeScript displays type Buffer = /* unresolved */ any;

总体来说,当 TypeScript 无法获取完整程序时,这能提供更流畅的体验。请记住,在常规情况下,当类型未找到时,你仍然会收到错误提示。

🌐 Altogether, this provides a smoother experience when TypeScript doesn’t have the full program available. Keep in mind, you’ll always get an error in regular scenarios to tell you when a type isn’t found.

欲了解更多信息,请在此查看实现

🌐 For more information, see the implementation here.

打破变更

🌐 Breaking Changes

lib.d.ts 变更

🌐 lib.d.ts Changes

TypeScript 4.5 对其内置声明文件进行了更改,这可能会影响你的编译;然而,这些更改相对较小,我们预计大多数代码不会受到影响。

🌐 TypeScript 4.5 contains changes to its built-in declaration files which may affect your compilation; however, these changes were fairly minimal, and we expect most code will be unaffected.

Awaited 的推断更改

🌐 Inference Changes from Awaited

由于 Awaited 现在在 lib.d.ts 中使用,并且由于 await,你可能会看到某些通用类型发生变化,这可能导致不兼容;然而,考虑到围绕 Awaited 进行的许多有意设计决策以避免破坏,我们预计大多数代码不会受到影响。

🌐 Because Awaited is now used in lib.d.ts and as a result of await, you may see certain generic types change that might cause incompatibilities; however, given many intentional design decisions around Awaited to avoid breakage, we expect most code will be unaffected.

tsconfig.json 根目录下的编译器选项检查

🌐 Compiler Options Checking at the Root of tsconfig.json

tsconfig.json 中不小心忘记 compilerOptions 部分是一个很容易犯的错误。 为了帮助发现这个错误,在 TypeScript 4.5 中,如果在没有在该 tsconfig.json 中定义 compilerOptions 的情况下,添加与 compilerOptions 中任何可用选项匹配的顶层字段,将会报错。

🌐 It’s an easy mistake to accidentally forget about the compilerOptions section in a tsconfig.json. To help catch this mistake, in TypeScript 4.5, it is an error to add a top-level field which matches any of the available options in compilerOptions without having also defined compilerOptions in that tsconfig.json.