TypeScript 5.0 新特性详解 - 装饰器与模块解析优化
装饰器
装饰器是 ECMAScript 的一项新功能,允许我们以可复用的方式自定义类及其成员。
考虑以下代码:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
const p = new Person("Ray");
p.greet();
greet
方法很简单,但假设它更复杂,比如包含异步逻辑、递归或副作用。无论具体实现如何,我们可能希望通过添加 console.log
调用来调试 greet
。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log("LOG: Entering method.");
console.log(`Hello, my name is ${this.name}.`);
console.log("LOG: Exiting method.");
}
}
这种模式很常见。如果能有一种方法为每个方法添加这样的日志记录功能就好了。这就是装饰器的作用。
我们可以编写一个名为 loggedMethod
的函数,如下所示:
function loggedMethod(originalMethod: any, _context: any) {
function replacementMethod(this: any, ...args: any[]) {
console.log("LOG: Entering method.");
const result = originalMethod.call(this, ...args);
console.log("LOG: Exiting method.");
return result;
}
return replacementMethod;
}
现在,我们可以使用 loggedMethod
来装饰 greet
方法:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
@loggedMethod
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
const p = new Person("Ray");
p.greet();
// 输出:
// LOG: Entering method.
// Hello, my name is Ray.
// LOG: Exiting method.
我们还可以利用上下文对象中的信息来打印被装饰方法的名称:
function loggedMethod(originalMethod: any, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
function replacementMethod(this: any, ...args: any[]) {
console.log(`LOG: Entering method '${methodName}'.`);
const result = originalMethod.call(this, ...args);
console.log(`LOG: Exiting method '${methodName}'.`);
return result;
}
return replacementMethod;
}
装饰器还可以用于属性、getter、setter 和自动访问器,甚至可以装饰类本身。
const
类型参数
当推断对象的类型时,TypeScript 通常会选择一个通用的类型。例如,names
的推断类型是 string[]
:
type HasNames = { names: readonly string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// 推断类型:string[]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
在 TypeScript 5.0 中,你可以在类型参数声明中添加 const
修饰符,使 const
类似的推断成为默认行为:
type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// 推断类型:readonly ["Alice", "Bob", "Eve"]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
在 extends
中支持多个配置文件
当管理多个项目时,有一个基础配置文件供其他 tsconfig.json
文件扩展会很有帮助。TypeScript 5.0 现在允许 extends
字段接受多个条目。例如:
{
"extends": ["./tsconfig1.json", "./tsconfig2.json"],
"compilerOptions": {
// ...
}
}
所有枚举都是联合枚举
TypeScript 5.0 通过为每个计算成员创建唯一类型,使所有枚举成为联合枚举。这意味着所有枚举现在都可以被缩小范围,并且它们的成员也可以作为类型引用。
模块解析捆绑器
TypeScript 4.7 引入了 node16
和 nodenext
选项,用于其 --module
和 --moduleResolution
设置。TypeScript 现在引入了一种新的策略:--moduleResolution bundler
。如果你使用的是现代捆绑器,如 Vite、esbuild、swc、Webpack、Parcel 等,新的 bundler
选项应该非常适合你。
解析自定义标志
允许导入 TS 扩展
--allowImportingTsExtensions
允许 TypeScript 文件使用 TypeScript 特定的扩展(如 .ts
、.mts
或 .tsx
)相互导入。
解析包 JSON 导出
--resolvePackageJsonExports
强制 TypeScript 在从 node_modules
中的包读取时,咨询 package.json
文件中的 exports
字段。
解析包 JSON 导入
--resolvePackageJsonImports
强制 TypeScript 在从文件开始以 #
的查找中咨询 package.json
文件中的 imports
字段。
允许任意扩展
--allowArbitraryExtensions
允许导入路径以未知的 JavaScript 或 TypeScript 文件扩展结束。
自定义条件
--customConditions
指定在 TypeScript 从 package.json
的 exports
或 imports
字段解析时应成功的其他条件。
原样模块语法
TypeScript 5.0 引入了一个新选项 --verbatimModuleSyntax
,使导入和导出的规则更加简单:任何没有 type
修饰符的导入或导出都会保留,而使用 type
修饰符的导入或导出将被完全删除。
支持 export type *
TypeScript 5.0 添加了对 export type * as ns from "module"
和 export type * from "module"
的支持。
JSDoc 中的 @satisfies
支持
TypeScript 5.0 在 JSDoc 中支持新的 @satisfies
标签,确保表达式的类型兼容,而不影响其自身类型。
JSDoc 中的 @overload
支持
TypeScript 5.0 允许在 JSDoc 中使用 @overload
标签声明函数的重载。
在 --build
下传递特定于 emit 的标志
TypeScript 现在允许在 --build
模式下传递以下标志:
--declaration
--emitDeclarationOnly
--declarationMap
--sourceMap
--inlineSourceMap
编辑器中的大小写不敏感导入排序
TypeScript 现在默认检测大小写敏感性,以解决在排序导入和导出时的不同解释问题。
穷尽的 switch
/case
补全
在编写 switch
语句时,TypeScript 会检测被检查值是否具有字面量类型,并提供补全以涵盖每个未覆盖的 case
。
速度、内存和包大小优化
TypeScript 5.0 在代码结构、数据结构和算法实现方面进行了大量改进,使整个体验更快,安装包也更小。
破坏性更改和弃用
运行时要求
TypeScript 现在针对 ECMAScript 2018。对于 Node 用户,这意味着至少需要 Node.js 10 及更高版本。
API 破坏性更改
在 TypeScript 5.0 中,我们迁移到模块,删除了一些不必要的接口,并进行了一些正确性改进。
关系运算符中的隐式类型转换被禁止
在 5.0 中,这也将应用于关系运算符 >
、<
、<=
和 >=
。
枚举大修
TypeScript 5.0 清理了一些长期存在的 enum
问题,并减少了理解各种 enum
类型所需的概念数量。
在构造函数中使用参数装饰器时更准确的类型检查
TypeScript 5.0 在 --experimentalDecorators
下对装饰器进行更准确的类型检查。
弃用和默认更改
在 TypeScript 5.0 中,我们弃用了以下设置和设置值:
--target: ES3
--out
--noImplicitUseStrict
--keyofStringsOnly
--suppressExcessPropertyErrors
--suppressImplicitAnyIndexErrors
--noStrictGenericChecks
--charset
--importsNotUsedAsValues
--preserveValueImports
- 项目引用中的
prepend
这些配置将继续被允许,直到 TypeScript 5.5,届时它们将被完全删除。
更多建议: