TypeScript 5.0 发布了啥?(非机翻)

415 阅读4分钟

TypeScript 5.0它终于来了,官链见:Announcing TypeScript 5.0 Beta,看看它主要更新了啥。

新的修饰器标准 - Decorators

decorators也将被ECMAScript原生支持,ts现版本已实验性的支持decorators好多年了,本次对此做了标准化调整。它介绍了几个例子,要表达的几点:

  1. decorators的第二个上下文参数context的介绍,它的类型是ClassMethodDecoratorContext
  2. context提供了addInitializer方法,它用于在class construct中注入执行
  3. 多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对allowImportingTsExtensionsresolvePackageJsonExports等配置项有所调整,具体见原文,这里介绍一个有意思的:如果开启了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列表