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.

不幸的是,在某些情况下这种分析方法不起作用。例如,如果变量在一个独立的函数中被访问,类型系统无法知道函数何时会被调用,因此会采取一种“乐观”的观点,认为变量已经被初始化。

🌐 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 的贡献!

相对路径的路径重写

🌐 Path Rewriting for Relative Paths

有几种工具和运行时可以让你“原地”运行 TypeScript 代码,这意味着它们不需要生成输出 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.

在使用这些模式时,有一些复杂性需要注意。\n为了与所有这些工具最大程度兼容,一个“就地”导入的 TypeScript 文件必须在运行时使用适当的 TypeScript 扩展名进行导入。\n例如,要导入一个名为 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());

另一个问题是(正如我们上面看到的)只有相对路径会被重写,而且它们是“天真地”被写入的。这意味着任何依赖 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 字段来解决。

🌐 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 的“命名空间”或“作用域”。 这是一种权宜之计,用来避免可能也使用 development 条件的依赖冲突。 如果每个人都在他们的包中提供一个 development,那么解析可能会尝试解析到一个 .ts 文件,而这不一定能正常工作。 这个想法类似于 Colin McDonnell 在其文章 TypeScript monorepo 中的动态类型 中描述的内容,以及 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 的贡献!

搜索祖级配置文件以确定项目所有权

🌐 Searching Ancestor Configuration Files for Project Ownership

当使用 TSServer(如 Visual Studio 或 VS Code)在编辑器中加载 TypeScript 文件时,编辑器会尝试找到“拥有”该文件的相关 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 是项目的“主”配置文件,而 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 识别为“所属”的配置文件——但那不是我们想要的! 如果遍历在此处停止,可能并不理想。 以前避免这种情况的唯一方法是将 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 不会生成“命名”导出,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;

请查看此处 以获取有关此更改的更多信息。

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 生成的类型可能会影响对代码库的类型检查。 更多信息,请参见与 DOM 和此版本 TypeScript 的 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.

在此处查看更多关于此更改的信息

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

🌐 More Implicit any Errors on Functions Returning null and undefined

当一个函数表达式被一个返回通用类型的签名作上下文类型时,TypeScript 现在会在 noImplicitAny 下适当地提供一个隐式的 any 错误,但在 strictNullChecks 之外不会。

🌐 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.

查看更多此更改的详情