TypeScript 5.0它终于来了,官链见:Announcing TypeScript 5.0 Beta,看看它主要更新了啥。
新的修饰器标准 - Decorators
decorators也将被ECMAScript原生支持,ts现版本已实验性的支持decorators好多年了,本次对此做了标准化调整。它介绍了几个例子,要表达的几点:
- decorators的第二个上下文参数context的介绍,它的类型是
ClassMethodDecoratorContext - context提供了
addInitializer方法,它用于在class construct中注入执行 - 多decorators的执行顺序是由后往前的
兼容性
ts以往版本需要开启--experimentalDecorators用于支持decorators,如果未开启,则对于decorators语法会报错。但在5.0后,无需此配置项的开启即可支持。 但是仍然建议开启此配置项,用于让ts去支持ESM标准以外的decorators特性。
类型
官方还特地嘱咐程序员们,不要让decorators丢失了类型,这里我要隆重的复制原例子,品读下类型之美!
function loggedMethod<This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
const methodName = String(context.name);
function replacementMethod(this: This, ...args: Args): Return {
console.log(`LOG: Entering method '${methodName}'.`)
const result = target.call(this, ...args);
console.log(`LOG: Exiting method '${methodName}'.`)
return result;
}
return replacementMethod;
}
const修饰类型 - const Type Parameters
一些程序员可能不知道const在类型中的奇妙用法,它结合as可以将类型断言成常量,如:
type HasNames = { readonly names: string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// Inferred type: string[]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"]});
if (names[0] === 'Hello') {
// code
}
在上面例子中,names得到的类型是string[], 所以if (names[0] === 'Hello')并不会有类型检查错误,但实际上任何一个程序员都看出来names的值只能是Alice等3个姓名,if条件并不会成立。在ts老版本中,我们修改下:
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"]} as const);
names得到的类型,就是常量本身readonly ["Alice", "Bob", "Eve"]。但是优雅永不过时,拒绝as是每个程序员的追求,在5.0中,可以用const修饰下类型了:
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
// code
}
继承多个配置文件 - Supporting Multiple Configuration Files in extends
背景:tsconfig.json的extends配置可以继承另一个配置文件。
以往,项目中存在 tsconfig.build.json extends from tsconfig.base.json extends from tsconfig.eslint.json 这样的配置继承关系,有一天你要加上tsconfig.fastbuild.json ,它只想extends from tsconfig.base.json,就是很棘手了。
在5.0中,extends支持多个配置文件了,如上场景就可以这样做,base和eslint都是独立的,没有继承关系,build和fastbuild分别如下:
// tsconfig.build.json
{"extends": ["./tsconfig.base.json", "./tsconfig.eslint.json"]}
// tsconfig.fastbuild.json
{"extends": ["./tsconfig.base.json"]}
// tsconfig.fastbuild.json
{"extends": ["./tsconfig.base.json"]}
注意,继承配置文件的优先级是后者覆盖前者。
enums联合类型 - All enums Are Union enums
好绕的小标题,不过内容很简单,说在远古时候,enums只能定义一组同类型的常量,如:
// 每个常量的类型必须一样,如下都是number
enum E {
Foo = 10,
Bar = 20,
}
在2.0之后呢,enum支持多类型了,如:
enum E {
Foo = 10,
Bar = '20',
}
但是呢,如果常量的类型是不确定的,ts会报错:
const a1 = '1';
enum E {
Foo = 1,
Bar = a1,
Car = Math.random()
}
5.0中,会推断出常量的类型。
模块解析策略支持bundler - moduleResolution bundler
你可以能看过一些esm和cjs之区别等文章弄的云里雾里的,更别谈什么.js、.mjs文件的迷宫引用了。但现在的vite、webpack等打包工具都自己搞定了不同文件的import策略,不用开发者关心,所以在5.0中提供了一个新的打包策略bundler,让你省去那些烦心。
跟随这个改动,5.0对allowImportingTsExtensions、resolvePackageJsonExports等配置项有所调整,具体见原文,这里介绍一个有意思的:如果开启了allowArbitraryExtensions,你可以使用{file basename}.d.{extension}.ts去描述ts不能识别的后缀文件的数据类型。例如:
/* app.css */
.cookie-banner {
display: none;
}
// app.d.css.ts
declare const css: {
cookieBanner: string;
};
export default css;
// App.tsx
import styles from "./app.css";
styles.cookieBanner; // string
导出所有type - Support for export type *
不多做介绍了,简单例子如下:
export type * as vehicles from "./spaceship";
支持satisfies - @satisfies Support in JSDoc
4.9的时候支持了satisfies,这个的作用是兼容某类型,举例:
interface CompilerOptions {
extends?: string | string[];
}
const o1: CompilerOptions = {
extends: [
"@tsconfig/strictest/tsconfig.json",
"../../../tsconfig.base.json"
],
};
const o2 = {
extends: [
"@tsconfig/strictest/tsconfig.json",
"../../../tsconfig.base.json"
],
} satisfies CompilerOptions;
在上面例子中,o2.extends类型推断是string[] ,它比any好在于可以更精确的推断类型。
因为历史原因很多人还在使用JSDoc,所以5.0在JSDoc里也支持了它。
switch语法补足 - Exhaustive switch/case Completions
一个小玩意,写第一个case后,ts给你补充完整的case列表
