8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScript 4.4の新機能

Posted at

先日、TypeScript 4.4のRCバージョンについてのアナウンスがありました。

Today we’re excited to announce our Release Candidate (RC) of TypeScript 4.4! Between now and the stable release of TypeScript 4.4, we expect no further changes apart from critical bug fixes.

そこで、本日はアナウンスの内容で気になった以下の4つを紹介したいと思います。

  1. Control Flow Analysis of Aliased Conditions and Discriminants
  2. Symbol and Template String Pattern Index Signatures
  3. Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)
  4. static Blocks in Classes

#1.Control Flow Analysis of Aliased Conditions and Discriminants

こちらは、Type Guardを今までよりも柔軟に使用するための変更です。

v4.3以前

function foo(arg: unknown) {
    const argIsString = typeof arg === "string";
    if (argIsString) {
        console.log(arg.toUpperCase());
        //              ~~~~~~~~~~~
        // Error! Property 'toUpperCase' does not exist on type 'unknown'.
    }
}

argIsStringが呼び出される段階でType Guardとしての機能が失われしまうため、errorが発生します。
v4.4からは、条件式の変数が評価される段階で、その変数にType Guardが含まれるかどうかを確認してくれるため、上記のようなerrorは発生しません。
これによって、一つの関数内で同じType Guardを何度も記載する必要がなくなります。

また、プロパティの値を用いてType Guardを実現できるようにもなります。
Enumと組み合わせれば便利な気がします。

type Shape =
    | { kind: "circle", radius: number }
    | { kind: "square", sideLength: number };

function area(shape: Shape): number {
    // Extract out the 'kind' field first.
    const { kind } = shape;

    if (kind === "circle") {
        // We know we have a circle here!
        return Math.PI * shape.radius ** 2;
    }
    else {
        // We know we're left with a square here!
        return shape.sideLength ** 2;
    }
}

ただし、以下のように関数の引数に分割代入が使用されている場合、Type Guardは機能しないので注意が必要です。

type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };

function foo({ kind, payload }: Data) {
    if (kind === 'str') {
        payload.length;  // Error, payload not narrowed to string
    }
}

#2. Symbol and Template String Pattern Index Signatures
objectのindex signaturesにSymbolやテンプレート文字列を使用可能になります。

  • Symbolの場合

objectのプロパティは常に衝突する危険性を孕んでいるので、objectを一意に特定するためのidなどはSymbolをkeyとしたプロパティに持たせておくのが良いかもしれないです。
Symbolをkeyとしたプロパティは、JSON.stringfy()Object.keys()で出力されないので注意

interface Colors {
    [sym: symbol]: number;
}

const red = Symbol("red");
const green = Symbol("green");
const blue = Symbol("blue");

let colors: Colors = {};

colors[red] = 255;          // Assignment of a number is allowed
let redVal = colors[red];   // 'redVal' has the type 'number'

colors[blue] = "da ba dee";
  • テンプレート文字列の場合

keyが特定のパターンで作成されている場合などに便利

interface Options {
    width?: number;
    height?: number;
}

let a: Options = {
    width: 100,
    height: 100,
    "data-blah": true, // Error! 'data-blah' wasn't declared in 'Options'.
};

interface OptionsWithDataProps extends Options {
    // Permit any property starting with 'data-'.
    [key: `data-${string}`]: boolean | number;
}

let b: OptionsWithDataProps = {
    width: 100,
    height: 100,
    "data-blah": true,       // Works!
    "data-num": 200,       // Works!

    "unknown-property": true,  // Error! 'unknown-property' wasn't declared in 'OptionsWithDataProps'.
};

#3.Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)

tsconfig.jsonの設定から、catchするerrorの型をデフォルトでunknownに指定することができるようになります。
これによって、catch句の中でerrorの型定義が可能となり、今までよりも型安全性が保たれます。

try {
    executeSomeThirdPartyCode();
}
catch (err) { // err: unknown

    // Error! Property 'message' does not exist on type 'unknown'.
    console.error(err.message);

    // Works! We can narrow 'err' from 'unknown' to 'Error'.
    if (err instanceof Error) {
        console.error(err.message);
    }
}

※ 今までもerrorにunknownを指定することはできましたが、毎回catch (err: unknown)と記載する必要がありました。

4.static Blocks in Classes

クラス内でstaticなブロックが使用可能になり、staticな変数を設定する際に複雑な処理を記載しやすくなります。
※ static BlocksはECMAScriptの仕様策定におけるStage3です。(2021/08/15時点)

class Foo {
    static Foo.count = 0;

    // This is a static block:
    static {
        if (someCondition()) {
            Foo.count++;
        }
    }
}

参照

https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-rc/
https://sosukesuzuki.dev/posts/stage-3-class-static-blocks/

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?