TypeScript 5.7

从未初始化的检查变量

¥Checks for Never-Initialized Variables

长期以来,TypeScript 一直能够捕获变量在所有先前分支中尚未初始化时的问题。

¥For a long time, TypeScript has been able to catch issues when a variable has not yet been initialized in all prior branches.

ts
let result: number
if (someCondition()) {
result = doSomeWork();
}
else {
let temporaryWork = doSomeWork();
temporaryWork *= 2;
// forgot to assign to 'result'
}
console.log(result); // error: Variable 'result' is used before being assigned.

遗憾的是,有些地方这种分析不起作用。例如,如果在单独的函数中访问变量,类型系统不知道何时调用该函数,而是从 “optimistic” 的角度来看,认为该变量将被初始化。

¥Unfortunately, there are some places where this analysis doesn’t work. For example, if the variable is accessed in a separate function, the type system doesn’t know when the function will be called, and instead takes an “optimistic” view that the variable will be initialized.

ts
function foo() {
let result: number
if (someCondition()) {
result = doSomeWork();
}
else {
let temporaryWork = doSomeWork();
temporaryWork *= 2;
// forgot to assign to 'result'
}
printResult();
function printResult() {
console.log(result); // no error here.
}
}

虽然 TypeScript 5.7 对可能已初始化的变量仍然比较宽容,但当变量从未初始化时,类型系统能够报告错误。

¥While TypeScript 5.7 is still lenient on variables that have possibly been initialized, the type system is able to report errors when variables have never been initialized at all.

ts
function foo() {
let result: number
// do work, but forget to assign to 'result'
function printResult() {
console.log(result); // error: Variable 'result' is used before being assigned.
}
}

此变更 的贡献要感谢 GitHub 用户 Zzzen

¥This change was contributed thanks to the work of GitHub user Zzzen!

相对路径的路径重写

¥Path Rewriting for Relative Paths

有几种工具和运行时允许你运行 TypeScript 代码 “in-place”,这意味着它们不需要生成输出 JavaScript 文件的构建步骤。例如,ts-node、tsx、Deno 和 Bun 都支持直接运行 .ts 文件。最近,Node.js 一直在研究对 --experimental-strip-types(即将取消标记!)和 --experimental-transform-types 的支持。这非常方便,因为它允许我们更快地进行迭代,而无需担心重新运行构建任务。

¥There are several tools and runtimes that allow you to run TypeScript code “in-place”, meaning they do not require a build step which generates output JavaScript files. For example, ts-node, tsx, Deno, and Bun all support running .ts files directly. More recently, Node.js has been investigating such support with --experimental-strip-types (soon to be unflagged!) and --experimental-transform-types. This is extremely convenient because it allows us to iterate faster without worrying about re-running a build task.

不过,使用这些模式时需要注意一些复杂性。为了最大限度地兼容所有这些工具,导入了 “in-place” 的 TypeScript 文件必须在运行时使用相应的 TypeScript 扩展名导入。例如,要导入一个名为 foo.ts 的文件,我们必须在 Node 的新实验性支持中编写以下内容:

¥There is some complexity to be aware of when using these modes though. To be maximally compatible with all these tools, a TypeScript file that’s imported “in-place” must be imported with the appropriate TypeScript extension at runtime. For example, to import a file called foo.ts, we have to write the following in Node’s new experimental support:

ts
// main.ts
import * as foo from "./foo.ts"; // <- we need foo.ts here, not foo.js

通常情况下,如果我们这样做,TypeScript 会报错,因为它要求我们导入输出文件。由于某些工具允许 .ts 导入,TypeScript 一段时间以来一直使用名为 --allowImportingTsExtensions 的选项来支持这种导入样式。这没问题,但如果我们需要从这些 .ts 文件中实际生成 .js 文件,会发生什么情况?对于需要仅分发 .js 文件的库作者来说,这是一项要求,但到目前为止,TypeScript 一直避免重写任何路径。

¥Typically, TypeScript would issue an error if we did this, because it expects us to import the output file. Because some tools do allow .ts imports, TypeScript has supported this import style with an option called --allowImportingTsExtensions for a while now. This works fine, but what happens if we need to actually generate .js files out of these .ts files? This is a requirement for library authors who will need to be able to distribute just .js files, but up until now TypeScript has avoided rewriting any paths.

为了支持这种情况,我们添加了一个名为 --rewriteRelativeImportExtensions 的新编译器选项。当导入路径为相对路径(以 ./../ 开头),以 TypeScript 扩展名(.ts.tsx.mts.cts)结尾,并且是非声明文件时,编译器会将路径重写为相应的 JavaScript 扩展名(.js.jsx.mjs.cjs)。

¥To support this scenario, we’ve added a new compiler option called --rewriteRelativeImportExtensions. When an import path is relative (starts with ./ or ../), ends in a TypeScript extension (.ts, .tsx, .mts, .cts), and is a non-declaration file, the compiler will rewrite the path to the corresponding JavaScript extension (.js, .jsx, .mjs, .cjs).

ts
// Under --rewriteRelativeImportExtensions...
// these will be rewritten.
import * as foo from "./foo.ts";
import * as bar from "../someFolder/bar.mts";
// these will NOT be rewritten in any way.
import * as a from "./foo";
import * as b from "some-package/file.ts";
import * as c from "@some-scope/some-package/file.ts";
import * as d from "#/file.ts";
import * as e from "./file.js";

这使我们能够编写可就地运行的 TypeScript 代码,并在准备就绪后将其编译成 JavaScript。

¥This allows us to write TypeScript code that can be run in-place and then compiled into JavaScript when we’re ready.

现在,我们注意到 TypeScript 通常避免重写路径。造成这种情况的原因有很多,但最明显的一个是动态导入。如果开发者编写了以下内容,则处理 import 接收的路径并非易事。事实上,在任何依赖中都无法覆盖 import 的行为。

¥Now, we noted that TypeScript generally avoided rewriting paths. There are several reasons for this, but the most obvious one is dynamic imports. If a developer writes the following, it’s not trivial to handle the path that import receives. In fact, it’s impossible to override the behavior of import within any dependencies.

ts
function getPath() {
if (Math.random() < 0.5) {
return "./foo.ts";
}
else {
return "./foo.js";
}
}
let myImport = await import(getPath());

另一个问题是(如上所述)只有相对路径会被重写,并且它们被写成 “naively”。这意味着任何依赖于 TypeScript 的 baseUrlpaths 的路径都不会被重写:

¥Another issue is that (as we saw above) only relative paths are rewritten, and they are written “naively”. This means that any path that relies on TypeScript’s baseUrl and paths will not get rewritten:

json
// tsconfig.json
{
"compilerOptions": {
"module": "nodenext",
// ...
"paths": {
"@/*": ["./src/*"]
}
}
}
ts
// Won't be transformed, won't work.
import * as utilities from "@/utilities.ts";

任何可能通过 package.jsonexportsimports 字段解析的路径也不会改变原始的 exports

¥Nor will any path that might resolve through the exports and imports fields of a package.json.

json
// package.json
{
"name": "my-package",
"imports": {
"#root/*": "./dist/*"
}
}
ts
// Won't be transformed, won't work.
import * as utilities from "#root/utilities.ts";

因此,如果你一直在使用工作区风格的布局,并且多个包相互引用,则可能需要将 条件导出作用域自定义条件 一起使用才能使其正常工作:

¥As a result, if you’ve been using a workspace-style layout with multiple packages referencing each other, you might need to use conditional exports with scoped custom conditions to make this work:

json
// my-package/package.json
{
"name": "my-package",
"exports": {
".": {
"@my-package/development": "./src/index.ts",
"import": "./lib/index.js"
},
"./*": {
"@my-package/development": "./src/*.ts",
"import": "./lib/*.js"
}
}
}

任何时候你想要导入 .ts 文件,都可以使用 node --conditions=@my-package/development 运行它。

¥Any time you want to import the .ts files, you can run it with node --conditions=@my-package/development.

请注意,我们在条件 @my-package/development 中使用的是 “namespace” 还是 “scope”。这是一个权宜之计,可以避免与可能也使用 development 条件的依赖发生冲突。如果每个人都在其软件包中附带 development 文件,则解析可能会尝试解析为 .ts 文件,但这不一定有效。这个想法与 Colin McDonnell 的文章 TypeScript 单体库中的实时类型tshy 从源代码加载指南 中描述的内容类似。

¥Note the “namespace” or “scope” we used for the condition @my-package/development. This is a bit of a makeshift solution to avoid conflicts from dependencies that might also use the development condition. If everyone ships a development in their package, then resolution may try to resolve to a .ts file which will not necessarily work. This idea is similar to what’s described in Colin McDonnell’s essay Live types in a TypeScript monorepo, along with tshy’s guidance for loading from source.

有关此功能如何工作的更多详细信息,请参阅 在此处阅读变更信息

¥For more specifics on how this feature works, read up on the change here.

支持 --target es2024--lib es2024

¥Support for --target es2024 and --lib es2024

TypeScript 5.7 现在支持 --target es2024,允许用户以 ECMAScript 2024 运行时为目标。此目标主要允许指定新的 --lib es2024,它包含 SharedArrayBufferArrayBufferObject.groupByMap.groupByPromise.withResolvers 等的许多功能。它还会将 Atomics.waitAsync--lib es2022 移至 --lib es2024

¥TypeScript 5.7 now supports --target es2024, which allows users to target ECMAScript 2024 runtimes. This target primarily enables specifying the new --lib es2024 which contains many features for SharedArrayBuffer and ArrayBuffer, Object.groupBy, Map.groupBy, Promise.withResolvers, and more. It also moves Atomics.waitAsync from --lib es2022 to --lib es2024.

请注意,作为 SharedArrayBufferArrayBuffer 更改的一部分,两者现在略有不同。为了弥补差距并保留底层缓冲区类型,所有 TypedArrays(如 Uint8Array 和其他)现在也是通用的

¥Note that as part of the changes to SharedArrayBuffer and ArrayBuffer, the two now diverge a bit. To bridge the gap and preserve the underlying buffer type, all TypedArrays (like Uint8Array and others) are now also generic.

ts
interface Uint8Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> {
// ...
}

每个 TypedArray 现在都包含一个名为 TArrayBuffer 的类型参数,但该类型参数有一个默认类型实参,以便我们可以继续引用 Int32Array 而无需显式写出 Int32Array<ArrayBufferLike>

¥Each TypedArray now contains a type parameter named TArrayBuffer, though that type parameter has a default type argument so that we can continue to refer to Int32Array without explicitly writing out Int32Array<ArrayBufferLike>.

如果你在本次更新中遇到任何问题,你可能需要更新 @types/node

¥If you encounter any issues as part of this update, you may need to update @types/node.

此工作 的提供主要要感谢 Kenta Moriuchi

¥This work was primarily provided thanks to Kenta Moriuchi!

搜索祖级配置文件以查找项目所有权

¥Searching Ancestor Configuration Files for Project Ownership

当使用 TSServer(例如 Visual Studio 或 VS Code)在编辑器中加载 TypeScript 文件时,编辑器将尝试查找与 “owns” 文件相关的 tsconfig.json 文件。为此,它会从正在编辑的文件开始向上遍历目录树,查找任何名为 tsconfig.json 的文件。

¥When a TypeScript file is loaded in an editor using TSServer (like Visual Studio or VS Code), the editor will try to find the relevant tsconfig.json file that “owns” the file. To do this, it walks up the directory tree from the file being edited, looking for any file named tsconfig.json.

以前,此搜索会在找到的第一个 tsconfig.json 文件处停止;但是,想象一下如下的项目结构:

¥Previously, this search would stop at the first tsconfig.json file found; however, imagine a project structure like the following:

project/
├── src/
│ ├── foo.ts
│ ├── foo-test.ts
│ ├── tsconfig.json
│ └── tsconfig.test.json
└── tsconfig.json

这里,src/tsconfig.json 是项目的 “main” 配置文件,而 src/tsconfig.test.json 是用于运行测试的配置文件。

¥Here, the idea is that src/tsconfig.json is the “main” configuration file for the project, and src/tsconfig.test.json is a configuration file for running tests.

json
// src/tsconfig.json
{
"compilerOptions": {
"outDir": "../dist"
},
"exclude": ["**/*.test.ts"]
}
json
// src/tsconfig.test.json
{
"compilerOptions": {
"outDir": "../dist/test"
},
"include": ["**/*.test.ts"],
"references": [
{ "path": "./tsconfig.json" }
]
}
json
// tsconfig.json
{
// This is a "workspace-style" or "solution-style" tsconfig.
// Instead of specifying any files, it just references all the actual projects.
"files": [],
"references": [
{ "path": "./src/tsconfig.json" },
{ "path": "./src/tsconfig.test.json" },
]
}

这里的问题是,当编辑 foo-test.ts 时,编辑器会将 project/src/tsconfig.json 视为 “owning” 配置文件。 - 但这不是我们想要的!如果遍历到此处停止,这可能并非理想情况。以前避免这种情况的唯一方法是将 src/tsconfig.json 重命名为类似 src/tsconfig.src.json 的名称,然后所有文件都会到达顶层 tsconfig.json,它引用了所有可能的项目。

¥The problem here is that when editing foo-test.ts, the editor would find project/src/tsconfig.json as the “owning” configuration file - but that’s not the one we want! If the walk stops at this point, that might not be desirable. The only way to avoid this previously was to rename src/tsconfig.json to something like src/tsconfig.src.json, and then all files would hit the top-level tsconfig.json which references every possible project.

project/
├── src/
│ ├── foo.ts
│ ├── foo-test.ts
│ ├── tsconfig.src.json
│ └── tsconfig.test.json
└── tsconfig.json

TypeScript 5.7 不再强制开发者这样做,而是继续遍历目录树,为编辑器场景查找其他合适的 tsconfig.json 文件。这可以为项目的组织方式和配置文件的结构提供更大的灵活性。

¥Instead of forcing developers to do this, TypeScript 5.7 now continues walking up the directory tree to find other appropriate tsconfig.json files for editor scenarios. This can provide more flexibility in how projects are organized and how configuration files are structured.

你可以在 GitHub 此处此处 上获取有关实现的更多详细信息。

¥You can get more specifics on the implementation on GitHub here and here.

编辑器中针对复合项目更快地进行项目所有权检查

¥Faster Project Ownership Checks in Editors for Composite Projects

想象一下一个具有以下结构的大型代码库:

¥Imagine a large codebase with the following structure:

packages
├── graphics/
│ ├── tsconfig.json
│ └── src/
│ └── ...
├── sound/
│ ├── tsconfig.json
│ └── src/
│ └── ...
├── networking/
│ ├── tsconfig.json
│ └── src/
│ └── ...
├── input/
│ ├── tsconfig.json
│ └── src/
│ └── ...
└── app/
├── tsconfig.json
├── some-script.js
└── src/
└── ...

packages 中的每个目录都是一个单独的 TypeScript 项目,而 app 目录是依赖于所有其他项目的主项目。

¥Each directory in packages is a separate TypeScript project, and the app directory is the main project that depends on all the other projects.

json
// app/tsconfig.json
{
"compilerOptions": {
// ...
},
"include": ["src"],
"references": [
{ "path": "../graphics/tsconfig.json" },
{ "path": "../sound/tsconfig.json" },
{ "path": "../networking/tsconfig.json" },
{ "path": "../input/tsconfig.json" }
]
}

现在注意到我们在 app 目录中有文件 some-script.js。当我们在编辑器中打开 some-script.js 时,TypeScript 语言服务(它也负责 JavaScript 文件的编辑器体验!)必须确定该文件属于哪个项目,以便应用正确的设置。

¥Now notice we have the file some-script.js in the app directory. When we open some-script.js in the editor, the TypeScript language service (which also handles the editor experience for JavaScript files!) has to figure out which project the file belongs to so it can apply the right settings.

在这种情况下,最近的 tsconfig.json 不包含 some-script.js,但 TypeScript 会继续询问“app/tsconfig.json 引用的项目之一是否可以包含 some-script.js?”。为此,TypeScript 之前会逐个加载每个项目,并在发现包含 some-script.js 的项目时立即停止。即使 some-script.js 不包含在根文件集中,TypeScript 仍然会解析项目中的所有文件,因为某些根文件集仍然可以传递引用 some-script.js

¥In this case, the nearest tsconfig.json does not include some-script.js, but TypeScript will proceed to ask “could one of the projects referenced by app/tsconfig.json include some-script.js?“. To do so, TypeScript would previously load up each project, one-by-one, and stop as soon as it found a project which contained some-script.js. Even if some-script.js isn’t included in the root set of files, TypeScript would still parse all the files within a project because some of the root set of files can still transitively reference some-script.js.

随着时间的推移,我们发现这种行为在较大的代码库中会导致极端且不可预测的行为。开发者可能会打开一些零散的脚本文件,并发现自己需要等待整个代码库被打开。

¥What we found over time was that this behavior caused extreme and unpredictable behavior in larger codebases. Developers would open up stray script files and find themselves waiting for their entire codebase to be opened up.

值得庆幸的是,每个可以被其他(非工作区)项目引用的项目都必须启用一个名为 composite 的标志,该标志强制执行一条规则,即所有输入源文件必须预先已知。因此,在探测 composite 项目时,TypeScript 5.7 将仅检查文件是否属于该项目的根文件集。这应该可以避免这种常见的最坏情况。

¥Thankfully, every project that can be referenced by another (non-workspace) project must enable a flag called composite, which enforces a rule that all input source files must be known up-front. So when probing a composite project, TypeScript 5.7 will only check if a file belongs to the root set of files of that project. This should avoid this common worst-case behavior.

更多信息请见 查看此处的变更

¥For more information, see the change here.

--module nodenext 中已验证的 JSON 导入

¥Validated JSON Imports in --module nodenext

--module nodenext 下从 .json 文件导入时,TypeScript 现在将强制执行某些规则以防止运行时错误。

¥When importing from a .json file under --module nodenext, TypeScript will now enforce certain rules to prevent runtime errors.

其次,任何 JSON 文件导入都需要包含 type: "json" 的导入属性。

¥For one, an import attribute containing type: "json" needs to be present for any JSON file import.

ts
import myConfig from "./myConfig.json";
// ~~~~~~~~~~~~~~~~~
// ❌ error: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'.
import myConfig from "./myConfig.json" with { type: "json" };
// ^^^^^^^^^^^^^^^^
// ✅ This is fine because we provided `type: "json"`

除了此验证之外,TypeScript 将不会生成 “named” 导出,并且 JSON 导入的内容只能通过默认值访问。

¥On top of this validation, TypeScript will not generate “named” exports, and the contents of a JSON import will only be accessible via a default.

ts
// ✅ This is okay:
import myConfigA from "./myConfig.json" with { type: "json" };
let version = myConfigA.version;
///////////
import * as myConfigB from "./myConfig.json" with { type: "json" };
// ❌ This is not:
let version = myConfig.version;
// ✅ This is okay:
let version = myConfig.default.version;

有关此变更的更多信息,请参阅 查看此处

¥See here for more information on this change.

支持 Node.js 中的 V8 编译缓存

¥Support for V8 Compile Caching in Node.js

Node.js 22 支持 一个名为 module.enableCompileCache() 的新 API。此 API 允许运行时重用工具首次运行后完成的部分解析和编译工作。

¥Node.js 22 supports a new API called module.enableCompileCache(). This API allows the runtime to reuse some of the parsing and compilation work done after the first run of a tool.

TypeScript 5.7 现在利用该 API,以便可以更快地开始执行有用的工作。在我们自己的一些测试中,我们发现运行 tsc --version 的速度提高了大约 2.5 倍。

¥TypeScript 5.7 now leverages the API so that it can start doing useful work sooner. In some of our own testing, we’ve witnessed about a 2.5x speed-up in running tsc --version.

Benchmark 1: node ./built/local/_tsc.js --version (*without* caching)
Time (mean ± σ): 122.2 ms ± 1.5 ms [User: 101.7 ms, System: 13.0 ms]
Range (min … max): 119.3 ms … 132.3 ms 200 runs
Benchmark 2: node ./built/local/tsc.js --version (*with* caching)
Time (mean ± σ): 48.4 ms ± 1.0 ms [User: 34.0 ms, System: 11.1 ms]
Range (min … max): 45.7 ms … 52.8 ms 200 runs
Summary
node ./built/local/tsc.js --version ran
2.52 ± 0.06 times faster than node ./built/local/_tsc.js --version

更多信息请见 查看此处的拉取请求

¥For more information, see the pull request here.

值得注意的行为变更

¥Notable Behavioral Changes

本节重点介绍了一系列值得注意的变更,在任何升级过程中都应确认并理解这些变更。有时它会高亮弃用、移除和新的限制。它还可以包含功能改进的错误修复,但这也可能通过引入新错误来影响现有构建。

¥This section highlights a set of noteworthy changes that should be acknowledged and understood as part of any upgrade. Sometimes it will highlight deprecations, removals, and new restrictions. It can also contain bug fixes that are functionally improvements, but which can also affect an existing build by introducing new errors.

lib.d.ts

为 DOM 生成的类型可能会对代码库的类型检查产生影响。更多信息请见 查看此版本 TypeScript 的 DOM 和 lib.d.ts 更新相关链接问题

¥Types generated for the DOM may have an impact on type-checking your codebase. For more information, see linked issues related to DOM and lib.d.ts updates for this version of TypeScript.

TypedArray 现在在 ArrayBufferLike 上是泛型

¥TypedArrays Are Now Generic Over ArrayBufferLike

在 ECMAScript 2024 中,SharedArrayBufferArrayBuffer 的类型略有不同。为了弥补差距并保留底层缓冲区类型,所有 TypedArrays(如 Uint8Array 和其他)现在也是通用的

¥In ECMAScript 2024, SharedArrayBuffer and ArrayBuffer have types that slightly diverge. To bridge the gap and preserve the underlying buffer type, all TypedArrays (like Uint8Array and others) are now also generic.

ts
interface Uint8Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> {
// ...
}

每个 TypedArray 现在都包含一个名为 TArrayBuffer 的类型参数,但该类型参数有一个默认类型实参,以便用户可以继续引用 Int32Array 而无需显式写出 Int32Array<ArrayBufferLike>

¥Each TypedArray now contains a type parameter named TArrayBuffer, though that type parameter has a default type argument so that users can continue to refer to Int32Array without explicitly writing out Int32Array<ArrayBufferLike>.

如果你在本次更新中遇到任何问题,例如

¥If you encounter any issues as part of this update, such as

error TS2322: Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike>'.
error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'Uint8Array<ArrayBufferLike>'.
error TS2345: Argument of type 'ArrayBufferLike' is not assignable to parameter of type 'ArrayBuffer'.
error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'string | ArrayBufferView | Stream | Iterable<string | ArrayBufferView> | AsyncIterable<string | ArrayBufferView>'.

那么你可能需要更新 @types/node

¥then you may need to update @types/node.

你可以阅读 GitHub 上有关此变更的详细信息

¥You can read the specifics about this change on GitHub.

从类中的非字面方法名称创建索引签名

¥Creating Index Signatures from Non-Literal Method Names in Classes

当类中的方法使用非字面量的计算属性名声明时,TypeScript 现在的行为更加一致。例如,在以下代码中:

¥TypeScript now has a more consistent behavior for methods in classes when they are declared with non-literal computed property names. For example, in the following:

ts
declare const symbolMethodName: symbol;
export class A {
[symbolMethodName]() { return 1 };
}

以前,TypeScript 只是以如下方式查看类:

¥Previously TypeScript just viewed the class in a way like the following:

ts
export class A {
}

换句话说,从类型系统的角度来看,[symbolMethodName]A 的类型没有任何贡献。

¥In other words, from the type system’s perspective, [symbolMethodName] contributed nothing to the type of A

TypeScript 5.7 现在能够更有意义地查看方法 [symbolMethodName]() {},并生成索引签名。因此,上面的代码将被解释为类似以下代码:

¥TypeScript 5.7 now views the method [symbolMethodName]() {} more meaningfully, and generates an index signature. As a result, the code above is interpreted as something like the following code:

ts
export class A {
[x: symbol]: () => number;
}

这提供了与对象字面量中的属性和方法一致的行为。

¥This provides behavior that is consistent with properties and methods in object literals.

点击此处了解更多关于此变更的信息

¥Read up more on this change here.

返回 nullundefined 的函数出现更多隐式 any 错误

¥More Implicit any Errors on Functions Returning null and undefined

当函数表达式通过返回泛型类型的签名进行上下文类型化时,TypeScript 现在会在 noImplicitAny 下(但在 strictNullChecks 之外)适当地提供隐式 any 错误。

¥When a function expression is contextually typed by a signature returning a generic type, TypeScript now appropriately provides an implicit any error under noImplicitAny, but outside of strictNullChecks.

ts
declare var p: Promise<number>;
const p2 = p.catch(() => null);
// ~~~~~~~~~~
// error TS7011: Function expression, which lacks return-type annotation, implicitly has an 'any' return type.

查看此变更的更多详细信息

¥See this change for more details.