TypeScript 5.0 中的新功能:声明符、常量类型、枚举改进、速度等等!

已发表: 2023-04-15

TypeScript 5.0 于 2023 年 3 月 16 日正式发布,现已开放给大家使用。 此版本引入了许多新功能,旨在使 TypeScript 更小、更简单、更快。

这个新版本对类定制的装饰器进行了现代化改造,允许以可重用的方式定制类及其成员。 开发人员现在可以将 const 修饰符添加到类型参数声明中,从而允许类似 const 的推理成为默认值。 新版本还使所有枚举联合枚举,简化代码结构并加快 TypeScript 体验。

在本文中,您将探索 TypeScript 5.0 中引入的变化,深入了解其新特性和功能。

TypeScript 5.0 入门

TypeScript 是一个官方编译器,您可以使用 npm 安装到您的项目中。 如果你想在你的项目中开始使用 TypeScript 5.0,你可以在你的项目目录中运行以下命令:

 npm install -D typescript

这会将编译器安装在node_modules目录中,您现在可以使用npx tsc命令运行该目录。

您还可以在本文档中找到有关在 Visual Studio Code 中使用较新版本的 TypeScript 的说明。

ICYMI:TypeScript 5.0 来了! 在本指南中探索其激动人心的更新,如声明符、常量类型和改进的枚举点击推文

TypeScript 5.0 有什么新功能?

在本文中,让我们探索 TypeScript 中引入的 5 个主要更新。 这些功能包括:

现代化的装饰器

装饰器已经在 TypeScript 中以实验性标志出现了一段时间,但新版本使它们跟上了 ECMAScript 提案的步伐,该提案现在处于第 3 阶段,这意味着它正处于添加到 TypeScript 的阶段。

装饰器是一种以可重用的方式自定义类及其成员的行为的方法。 例如,如果您的类有两个方法, greetgetAge

 class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();

在现实世界的用例中,此类应该有更复杂的方法来处理一些异步逻辑并有副作用等,你会想在其中加入一些console.log调用来帮助调试这些方法。

 class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log('LOG: Method Execution Starts.'); console.log(`Hello, my name is ${this.name}.`); console.log('LOG: Method Execution Ends.'); } getAge() { console.log('LOG: Method Execution Starts.'); console.log(`I am ${this.age} years old.`); console.log('Method Execution Ends.'); } } const p = new Person('Ron', 30); p.greet(); p.getAge();

这是一个经常出现的模式,如果有一个解决方案可以应用于每个方法会很方便。

这就是装饰器发挥作用的地方。 我们可以定义一个名为debugMethod的函数,如下所示:

 function debugMethod(originalMethod: any, context: any) { function replacementMethod(this: any, ...args: any[]) { console.log('Method Execution Starts.'); const result = originalMethod.call(this, ...args); console.log('Method Execution Ends.'); return result; } return replacementMethod; }

在上面的代码中, debugMethod采用原始方法 ( originalMethod ) 并返回执行以下操作的函数:

  1. 记录消息“方法执行开始”。
  2. 传递原始方法及其所有参数(包括 this)。
  3. 记录消息“方法执行结束”。
  4. 返回原始方法返回的任何内容。

通过使用装饰器,您可以将debugMethod应用于您的方法,如下面的代码所示:

 class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } @debugMethod greet() { console.log(`Hello, my name is ${this.name}.`); } @debugMethod getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();

这将输出以下内容:

 LOG: Entering method. Hello, my name is Ron. LOG: Exiting method. LOG: Entering method. I am 30 years old. LOG: Exiting method.

在定义装饰器函数 ( debugMethod ) 时,会传递第二个参数,称为context (它是上下文对象——有一些关于如何声明装饰方法以及方法名称的有用信息)。 您可以更新debugMethod以从context对象中获取方法名称:

 function debugMethod( originalMethod: any, context: ClassMethodDecoratorContext ) { const methodName = String(context.name); function replacementMethod(this: any, ...args: any[]) { console.log(`'${methodName}' Execution Starts.`); const result = originalMethod.call(this, ...args); console.log(`'${methodName}' Execution Ends.`); return result; } return replacementMethod; }

当您运行您的代码时,输​​出现在将包含使用debugMethod装饰器装饰的每个方法的名称:

 'greet' Execution Starts. Hello, my name is Ron. 'greet' Execution Ends. 'getAge' Execution Starts. I am 30 years old. 'getAge' Execution Ends.

您可以使用装饰器做更多的事情。 请随时查看原始拉取请求,了解有关如何在 TypeScript 中使用装饰器的更多信息。

引入 const 类型参数

这是另一个重要版本,它为您提供了一个带有泛型的新工具,以改进您在调用函数时获得的推断。 默认情况下,当您使用const声明值时,TypeScript 会推断类型而不是其文字值:

 // Inferred type: string[] const names = ['John', 'Jake', 'Jack'];

到目前为止,要实现所需的推理,您必须通过添加“as const”来使用 const 断言:

 // Inferred type: readonly ["John", "Jake", "Jack"] const names = ['John', 'Jake', 'Jack'] as const;

当你调用函数时,它是相似的。 在下面的代码中,国家的推断类型是string[]

 type HasCountries = { countries: readonly string[] }; function getCountriesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: string[] const countries = getCountriesExactly({ countries: ['USA', 'Canada', 'India'] });

您可能需要一种更具体的类型,之前修复的一种方法是添加as const断言:

 // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] } as const);

这可能很难记住和实施。 但是,TypeScript 5.0 引入了一项新功能,您可以在其中将 const 修饰符添加到类型参数声明中,这将默认自动应用类似 const 的推断。

 type HasCountries = { countries: readonly string[] }; function getNamesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] });

使用const类型参数可以让开发人员在代码中更清楚地表达意图。 如果一个变量是常量并且永远不会改变,那么使用const类型参数可以确保它永远不会被意外改变。

您可以查看原始拉取请求,了解有关 const 类型参数如何在 TypeScript 中工作的更多信息。

枚举的改进

TypeScript 中的枚举是一种强大的结构,允许开发人员定义一组命名常量。 在 TypeScript 5.0 中,对枚举进行了改进,使其更加灵活和有用。

例如,如果您将以下枚举传递给函数:

 enum Color { Red, Green, Blue, } function getColorName(colorLevel: Color) { return colorLevel; } console.log(getColorName(1));

在引入 TypeScript 5.0 之前,您可以传递错误的级别编号,但不会抛出任何错误。 但是随着 TypeScript 5.0 的引入,它会立即抛出错误。

此外,新版本通过为每个计算成员创建唯一类型,使所有枚举成为联合枚举。 此增强允许缩小所有枚举并将其成员引用为类型:

 enum Color { Red, Purple, Orange, Green, Blue, Black, White, } type PrimaryColor = Color.Red | Color.Green | Color.Blue; function isPrimaryColor(c: Color): c is PrimaryColor { return c === Color.Red || c === Color.Green || c === Color.Blue; } console.log(isPrimaryColor(Color.White)); // Outputs: false console.log(isPrimaryColor(Color.Red)); // Outputs: true

TypeScript 5.0 的性能改进

TypeScript 5.0 在代码结构、数据结构和算法扩展方面进行了大量重大更改。 这有助于改善从安装到执行的整个 TypeScript 体验,使其更快、更高效。

例如,TypeScript 5.0 和 4.9 的包大小之间的差异是相当可观的。

TypeScript 最近从命名空间迁移到模块,允许它利用现代构建工具来执行范围提升等优化。 此外,删除一些已弃用的代码已将 TypeScript 4.9 的 63.8 MB 包大小减少了约 26.4 MB。

TypeScript 包大小
TypeScript 包大小

以下是 TypeScript 5.0 和 4.9 在速度和大小方面的一些更有趣的胜利:

设想时间或大小相对于 TS 4.9
material-ui 构建时间90%
TypeScript 编译器启动时间89%
编剧建造时间88%
TypeScript Compiler 自建时间87%
Outlook Web 构建时间82%
VS 代码构建时间80%
打字稿 npm 包大小59%

Bundler Resolution 以获得更好的模块解析

当您在 TypeScript 中编写 import 语句时,编译器需要知道 import 指的是什么。 它使用模块解析来做到这一点。 例如,当您编写import { a } from "moduleA"时,编译器需要知道amoduleA中的定义以检查其使用。

在 TypeScript 4.7 中,为--modulemoduleResolution设置添加了两个新选项: node16nodenext

这些选项的目的是更准确地表示 Node.js 中 ECMAScript 模块的确切查找规则。 但是,此模式有一些其他工具未强制执行的限制。

例如,在 Node.js 的 ECMAScript 模块中,任何相对导入都必须包含文件扩展名才能正常工作:

 import * as utils from "./utils"; // Wrong import * as utils from "./utils.mjs"; // Correct

TypeScript 引入了一种名为“moduleResolution bundler”的新策略。 可以通过在 TypeScript 配置文件的“compilerOptions”部分添加以下代码来实现此策略:

 { "compilerOptions": { "target": "esnext", "moduleResolution": "bundler" } }

这种新策略适用于那些使用现代打包器的人,例如 Vite、esbuild、swc、Webpack、Parcel 和其他使用混合查找策略的打包器。

您可以查看原始拉取请求及其实现,以了解有关moduleResolution捆绑器如何在 TypeScript 中工作的更多信息。

弃用

TypeScript 5.0 有一些贬值,包括运行时要求、lib.d.ts 更改和 API 重大更改。

  1. 运行时要求: TypeScript 现在以 ECMAScript 2018 为目标,该包将最低引擎期望设置为 12.20。 因此,Node.js 用户的最低版本应为 12.20 或更高版本才能使用 TypeScript 5.0。
  2. lib.d.ts 变化: DOM 类型的生成方式发生了一些变化,这可能会影响现有代码。 特别是,某些属性已从数字转换为数字文字类型,并且用于剪切、复制和粘贴事件处理的属性和方法已跨接口移动。
  3. API 重大更改:删除了一些不必要的接口,并进行了一些正确性改进。 TypeScript 5.0 也转移到了模块中。

TypeScript 5.0 弃用了某些设置及其相应的值,包括target: ES3outnoImplicitUseStrictkeyofStringsOnlysuppressExcessPropertyErrorssuppressImplicitAnyIndexErrorsnoStrictGenericCheckscharsetimportsNotUsedAsValuespreserveValueImports ,以及项目引用中的前缀。

虽然这些配置在 TypeScript 5.5 之前仍然有效,但会发出警告以提醒仍在使用它们的用户。

TypeScript 5.0 更简单、更快、更小! 在这里探索将彻底改变您的编码游戏的变化。 点击推文

概括

在本文中,您了解了 TypeScript 5.0 带来的一些主要功能和改进,例如对枚举、捆绑器解析和 const 类型参数的改进,以及对速度和大小的改进。

如果您正在考虑为您的下一个项目使用 TypeScript,请免费试用 Kinsta 的应用程序托管。

现在轮到你了! 您觉得 TypeScript 5.0 中哪些功能或改进最吸引人? 有没有我们可能忽略的重要内容? 让我们在评论中知道。