TypeScript 5.0 中的新功能:声明符、常量类型、枚举改进、速度等等!
已发表: 2023-04-15TypeScript 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 的说明。
TypeScript 5.0 有什么新功能?
在本文中,让我们探索 TypeScript 中引入的 5 个主要更新。 这些功能包括:
现代化的装饰器
装饰器已经在 TypeScript 中以实验性标志出现了一段时间,但新版本使它们跟上了 ECMAScript 提案的步伐,该提案现在处于第 3 阶段,这意味着它正处于添加到 TypeScript 的阶段。
装饰器是一种以可重用的方式自定义类及其成员的行为的方法。 例如,如果您的类有两个方法, greet
和getAge
:
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
) 并返回执行以下操作的函数:
- 记录消息“方法执行开始”。
- 传递原始方法及其所有参数(包括 this)。
- 记录消息“方法执行结束”。
- 返回原始方法返回的任何内容。
通过使用装饰器,您可以将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 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"
时,编译器需要知道a
在moduleA
中的定义以检查其使用。
在 TypeScript 4.7 中,为--module
和moduleResolution
设置添加了两个新选项: node16
和nodenext
。
这些选项的目的是更准确地表示 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 重大更改。
- 运行时要求: TypeScript 现在以 ECMAScript 2018 为目标,该包将最低引擎期望设置为 12.20。 因此,Node.js 用户的最低版本应为 12.20 或更高版本才能使用 TypeScript 5.0。
- lib.d.ts 变化: DOM 类型的生成方式发生了一些变化,这可能会影响现有代码。 特别是,某些属性已从数字转换为数字文字类型,并且用于剪切、复制和粘贴事件处理的属性和方法已跨接口移动。
- API 重大更改:删除了一些不必要的接口,并进行了一些正确性改进。 TypeScript 5.0 也转移到了模块中。
TypeScript 5.0 弃用了某些设置及其相应的值,包括target: ES3
、 out
、 noImplicitUseStrict
、 keyofStringsOnly
、 suppressExcessPropertyErrors
、 suppressImplicitAnyIndexErrors
、 noStrictGenericChecks
、 charset
、 importsNotUsedAsValues
和preserveValueImports
,以及项目引用中的前缀。
虽然这些配置在 TypeScript 5.5 之前仍然有效,但会发出警告以提醒仍在使用它们的用户。
概括
在本文中,您了解了 TypeScript 5.0 带来的一些主要功能和改进,例如对枚举、捆绑器解析和 const 类型参数的改进,以及对速度和大小的改进。
如果您正在考虑为您的下一个项目使用 TypeScript,请免费试用 Kinsta 的应用程序托管。
现在轮到你了! 您觉得 TypeScript 5.0 中哪些功能或改进最吸引人? 有没有我们可能忽略的重要内容? 让我们在评论中知道。