Angular 15 更新后的 Typescript 目标警告

26

我将我的 Angular 应用程序更新到 Angular 15。 它构建正常 - 除了一些警告,如下:

Angular CLI 将 TypeScript 编译器选项“target”和“useDefineForClassFields”分别设置为“ES2022”和“false”。

我的 tsconfig.json 将目标设置为 ES6

{
  ...
  "compilerOptions": {
      "target": "ES6",
      ...
  }
}

文档中提到:

内部上,Angular CLI 现在总是将 TypeScript 目标设置为 ES2022,并将 useDefineForClassFields 设置为 false,除非在 TypeScript 配置中将目标设置为 ES2022 或更高版本。

https://github.com/angular/angular-cli/blob/main/CHANGELOG.md

而我的 .browserslistrc 文件自从一开始以来就没有变化过,已经有好几个月了:

last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR

那么,我该如何消除这个警告?


参考此处的 PR https://github.com/angular/angular-cli/pull/23936。 - Simon_Weaver
3个回答

31
为了理解这个警告,我们首先需要了解 Angular 构建的工作原理。
首先,需要运行 TypeScript 构建。在此步骤中,使用 tsconfig.json 中的设置将 TypeScript 代码编译为 JavaScript。如果将 target 设置为 ES6,则在 Angular 15 之前,TypeScript 构建将生成 ES6 代码。
随后,Angular 使用 Babel 将生成的 JavaScript 代码向后兼容旧版浏览器,详见 文档。它会通过 browserslist 配置获取您希望支持的浏览器列表,并在必要时添加缺失功能的 polyfill。

回到 Angular 支持 IE11 的时代,这个过程变得更加复杂了。Angular CLI 甚至为 IE11 生成了一个额外的捆绑包,这被称为 differential loading。但现在 Angular 已经放弃了对 IE11 的支持,他们可以再次简化事情并朝着现代捆绑包的方向发展,这可能是他们在 v15 中进行更改的原因。

所以在我看来,没有什么好理由将 tsconfig.json 中的 target 设置为像 ES6 这样的旧版本。现代浏览器现在支持更多的 EcmaScript 特性,使用更更新的 EcmaScript 版本将使您的捆绑包大小更小。Babel 将填充缺失的功能,因此您不必担心。只需按照他们的建议将目标设置为 ES2022 即可。


你知道有哪些例子可以说明使用ES2020和ES2022编译的项目会有什么不同吗?Babel的polyfill主要是为了修补浏览器API,而TS编译是否需要以不同的方式重写代码呢? - Simon_Weaver
直到 Angular 15,我一直在使用 ES6 目标,以便 ?? 或 ?. 可以重写以实现 Safari 13.3 兼容性。我现在可以放弃这一点,但如果这意味着可能会影响一些 Safari 14(或任何其他版本)用户,我并不一定舒服地跳转到 ES2022。 - Simon_Weaver
1
@Simon_Weaver babel 对 TypeScript 编译没有任何影响。它应该根据需要填充缺失的浏览器 API 和功能,包括 nullish coalescing,只需相应地配置您的 browserslist 即可。此外,Angular 仅支持 Safari 的最近两个主要版本。我们已经使用 Safari 16,因此 13 和 14 已经不再受支持。 - JSON Derulo
3
谢谢,但是“官方”支持14和通过使用较低的“目标”(或更宽松的浏览器文件)来支持14之间存在差异。这正是我昨天之前一直在做的,以支持旧版Safari中的???. - 我们仍然从Safari 13和14中获得收益!只是很难在应用程序中准确地导航每个选项的影响,但我个人认为我不会在一段时间内使用ES2022。 - Simon_Weaver
我点赞了你的答案,因为它写得很好,深入浅出。我刚转到Angular15,所以这个IE支持问题对我来说完全是个谜。与Angular合作很难,因为他们每几个月就改变目标。我认为这个框架可能会在下一个十年中逐渐消失,因为它无法帮助那些卡在旧版本上的开发者。这是非常糟糕的架构和应用程序设计模型。我现在改用Microsoft Blazor,它更简单易用,使用真正的面向对象语言。 - Stokely
@JSONDerulo 这对我非常有帮助。我正在调试电视浏览器(三星和安卓电视)上的一个问题,问题是代码中使用了??=这样的运算符,大多数浏览器不支持。所以我不得不导出browserlist并将其设置为last 4 Safari major versions以摆脱??=。然而,这不是一个永久解决方案,因为当我们升级到Safari 17时,我需要将其更新为Safari的最新5个版本。有没有办法强制Babel导出到es2020,这样我就不需要使用browserlist了? - undefined

12
我曾经遇到同样的问题,并通过在我的tsconfig中添加"target": "ES2022"和"useDefineForClassFields": false成功消除了这个警告。是否这是一个好主意还需要等待比我更有经验的人的评论。我担心当2022变成2023(或者下一个版本)时,这种方法也会失败。如果Angular已经覆盖它,最好完全留出来。但我可能对这个问题理解还不够完整。
在你的情况下,你应该能够做到(或者至少尝试)相同的事情,代替ES6(我理解与ES2015相同)。根据你引用的文档,无论你的请求如何,Angular都是这样做的,所以如果你只收到警告而没有错误,你的代码应该没问题。如果你需要进一步限制到ES6级别,似乎需要使用你的.broswerslistrc文件来完成,这也可能已经可以了。

我认为这里的问题在于警告并不是很有帮助,至少对像你和我这样的人来说是这样,我们是接收到警告却不知道该怎么做。此外,随后的网页链接(“要控制 ECMA 版本和功能,请使用 Browerslist 配置。有关更多信息,请参见https://angular.io/guide/build#configuring-browser-compatibility”)似乎并没有特别有助于解决警告问题,它告诉我们应该做什么,但并没有告诉我们如何消除警告。


1
Angular使用Babel来确保与.browserslistrc中定义的浏览器的向后兼容性。因此,可以放心使用现代的tsconfig目标,Babel会添加缺失功能的polyfills。更多详细信息,请参阅我的答案。 - JSON Derulo

6

编辑:事实证明,与此警告相关的一个错误已经被修复,在Angular CLI 16.0.4中得到了解决。对于Angular CLI 15-16.0.3,请确保设置target="ES2022"以避免下面描述的不匹配。使用ng version检查您的CLI版本。


小心不要误解警告及其后果!

完整的消息(截至Angular 16)如下:

TypeScript编译器选项“target”和“useDefineForClassFields”由Angular CLI分别设置为“ES2022”和“false”。 要控制ECMA版本和功能,请使用Browerslist配置。有关详细信息,请参见https://angular.io/guide/build#configuring-browser-compatibility 注意:您可以在项目的tsconfig中将“target”设置为“ES2022”以删除此警告。

其中一个相当合理的解释可能是:

Angular正在忽略我在您的tsconfig.json中提供的compilerOptions.target,并将我的代码编译为ES2022。

然而,这不是发生的事情!

为了确认,我创建了一个简单的ng new项目,并将目标更新为ES2015:

compilerOptions: {
   "target": "ES2015"
}

我在我的AppComponent中编写了以下代码行,使用了nullish coalescing operator,这是ES2022的一个特性。
const cat = null;
const dog = { catName: 'Kim' };

const animal = cat ?? dog;

查看通过ng serve在浏览器中运行的.js编译代码(而不是sourcemapped .ts文件),您将看到以下内容-表明??已经被转译为与旧版浏览器兼容:

 const animal = cat !== null && cat !== void 0 ? cat : dog;

当然,ES2022目标不能直接实现这一点。

假设您确实需要支持Safari 13.0(不支持??),并认为一切都很好。

错了!

在浏览器中搜索??,您会看到许多在vendor.js文件中使用??的地方。它在那里没有被转译,因此您的应用程序将无法在Safari 13.0上运行。

这就是浏览器列表的作用,如果配置正确,将为特定目标转译整个应用程序。


实际上这个消息的含义是什么:

您的应用程序代码正在编译为比ES2022更旧的目标,这可能导致您的代码与供应商代码不匹配。我们建议将您的目标设置为ES2022,并使用独占的browserslist来控制旧版浏览器的转译,以确保所有代码都按预期进行转译。


为什么我还是困惑:

引入此功能的以下提交似乎将目标设置为ES2022,但实际上并没有对应用程序代码产生影响。不确定原因。

https://github.com/angular/angular-cli/pull/23936/commits/cdac700b93d5593ce92b44b1691941673eaf25b0#diff-2d55449c69797885b38f6b957a1fd03d27beda366a9270293719ec041cf15d0f


我认为这种行为并不是意图中的,而且我已经提交了一个错误报告 https://github.com/angular/angular-cli/issues/25290。 - Simon_Weaver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接