Typescript新特性 [4.3 ~ 5.2]
2023-06-24
Coding
Typescript
👋 ‍️‍️阅读
❤️ 喜欢
💬 评论

Typescript新特性 [4.3 ~ 5.2]

Typescript一直保持着3个月一个release,从2021年5月至今发布了10个版本(5.2将在8月release)。 本文盘点各个版本中的主要更新,着重在语言特性,其他有关性能等其他方面的更新请自行查看路线图。

Typescript Roadmap

4.3

分离的setter类型

允许getter和setter指定不同的类型。 下例中,size是一个数字,但是同时setter可以接受字符串并转换。

4.3版本中依然限制getter返回类型必须可转换为setter入参类型。这一限制在5.1版本中进一步宽松,允许getter与setter的类型完全无关,Unrelated types for getters and setters

Playground

class Thing {
  #size = 0;

  get size(): number {
    return this.#size;
  }

  set size(value: string | number | boolean) {
    let num = Number(value);

    if (!Number.isFinite(num)) {
      this.#size = 0;
      return;
    }

    this.#size = num;
  }
}

const thing = new Thing();

thing.size = '123';
console.log(thing.size); // 123

thing.size = 'abc';
console.log(thing.size); // 0

#private私有元素

允许使用#前缀定义类的私有元素,满足了ECMA标准。

MDN - Private class fields

相比于private关键字,#privte元素在运行时也是私有的,更为安全。

class Foo {
  #someField = 0;

  #someMethod() {
  }
}

override关键字

  • 新增override关键字,标识一个方法覆盖了父类方法。
  • 并可以通过--noImplicitOverrideflag来禁止隐式的覆盖。

Playground

class SomeComponent {
  show() {
  }
  hide() {
  }
}

class SpecializedComponent extends SomeComponent {
    override show() {
    }

    // ts error
    hide() {
    }
}

其他

  • strictNullCheck下,对Promise进行null check需要先await。Doc
  • 允许定义静态索引签名,即static [key: string]: stringDoc

4.4

控制流中的类型分析

Typescript可以进行更加复杂且精准的类型判断,使得控制流中的(主要是if-else)类型更加精确。

由于涉及的情况多种多样,请自行查看官方实例

static block

类中允许定义静态代码块。

Playground

// Prints:
//    1
//    2
//    3
class Foo {
    static prop = 1
    static {
        console.log(Foo.prop++);
    }
    static {
        console.log(Foo.prop++);
    }
    static {
        console.log(Foo.prop++);
    }
}

其他

  • symbol和模板字符串可以作为索引签名,即[key: symbol]: stringDoc
  • 新增--useUnknownInCatchVariablesflag,使得catch类型默认为unknown(原本是any)。Doc

4.5

Awaited工具类

新增工具类Awaited用于递归地得到最终的Promise泛型。

// A = string
type A = Awaited<Promise<string>>;
// B = number
type B = Awaited<Promise<Promise<number>>>;
// C = boolean | number
type C = Awaited<boolean | Promise<number>>;

其他

  • 模板字符串可以用于缩小类型。Doc
  • 条件类型的尾递归优化,使得可以嵌套的层数明显提升,例如此问题Doc
  • 私有字段#private可以进行in检查并用于类型断言。Doc
export interface Success {
    type: `${string}Success`;
    body: string;
}

export interface Error {
    type: `${string}Error`;
    message: string
}

export function handler(r: Success | Error) {
    if (r.type === "HttpSuccess") {
        const token = r.body; // r is Success
    }
}

4.6

  • 允许在super()前调用代码(仍然不能使用this)。Doc
  • 解构后控制流分析的类型断言依然有效。Doc

4.7

Typescript 4.7主要改动是针对ESM和CJS的兼容性问题。

4.8

允许infer进行extend约束

现在infer类型内可以进行extend约束(仅基本类型),使得推导类型进行类型转换。

Playground

type SomeNum = "100" extends `${infer U extends number}` ? U : never;

type SomeBool = "true" extends `${infer U extends boolean}` ? U : never;

4.9

satisfied关键字

新增satisified关键字,可以看作是只做约束而不作转换的as操作。 这一操作对于定义常量(一般是映射用)非常有用。

具体示例参见官方文档

in缩小类型

in关键字现在会合并检查的key以缩小类型,见下例。

Playground

interface Context {
    packageJSON: unknown;
}
function tryGetPackageName(context: Context): string | undefined {
    const packageJSON = context.packageJSON;
    // Check to see if we have an object.
    if (packageJSON && typeof packageJSON === "object") {
        // Check to see if it has a string name property.
        if ("name" in packageJSON && typeof packageJSON.name === "string") {
            // Not work before 4.9
            return packageJSON.name;
        }
    }
    return undefined;
}

其他

  • 新增accessor关键词,自动生成getter/setter(不知道有什么用)。Doc
  • 自动检查错误的===NaNDoc

5.0

Decorator

Typescript 5.0是一个非常大的版本更新,主要包括了Decorator装饰器。

装饰器还未正式纳入ECMA,现在正处在Stage 3。ECMA Proposals

装饰器给了js/ts又一次大的变化,很多高级便捷的功能成为了可能。下面的例子展示了一个简单的@loggedMethod功能。

Playground

function loggedMethod(headMessage = "LOG:") {
    return function actualDecorator(originalMethod: any, context: ClassMethodDecoratorContext) {
        const methodName = String(context.name);
        function replacementMethod(this: any, ...args: any[]) {
            console.log(`${headMessage} Entering method '${methodName}'.`)
            const result = originalMethod.call(this, ...args);
            console.log(`${headMessage} Exiting method '${methodName}'.`)
            return result;
        }
        return replacementMethod;
    }
}

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();

const泛型

在泛型上可以定义const修饰符,编译器将尽可能的匹配常量类型,这使得调用者不需要总是显式的写上as const

Playground

declare function foo<const T extends readonly string[]>(args: T): void;

// T is readonly ["a", "b", "c"]
foo(["a", "b" ,"c"]);

5.1

  • 现在没有return的函数可以隐含地匹配undefined返回类型。Doc
  • getter可以和setter类型无关(上文有提到)。Doc

5.2

目前5.2版本尚未release,版本计划见TypeScript 5.2 Iteration Plan

using关键字

https://github.com/microsoft/TypeScript/pull/54505

using也是一个ECMA新标准,处于Stage 3。

using类似const,定义一个可以被释放的常量。在离开作用域时,using对象的Symbol.dispose方法会被调用。

同时还支持异步的await using

function foo() {
  const getResource = () => {
    return {
      [Symbol.dispose]: () => {
        console.log('disposed')
      }
    }
  }
  using resource = getResource();
  // resource will be disposed when return
}

Decorator Metadata

https://github.com/microsoft/TypeScript/issues/53461

允许装饰器在目标对象上添加元信息。

function meta(key, value) {
  return (_, context) => {
    context.metadata[key] = value;
  };
}

@meta('a' 'x')
class C {
  @meta('b', 'y')
  m() {}
}

C[Symbol.metadata].a; // 'x'
C[Symbol.metadata].b; // 'y'

Copyright © 2020-2024 Dean Xu. All Rights reserved.