TypeScript 4.4の新機能

先日、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.


https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-rc/

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

1. [Control Flow Analysis of Aliased Conditions and Discriminants](#1control-flow-analysis-of-aliased-conditions-and-discriminants)
2. [Symbol and Template String Pattern Index Signatures](#2-symbol-and-template-string-pattern-index-signatures)
3. [Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)](#3defaulting-to-the-unknown-type-in-catch-variables---useunknownincatchvariables)
4. [static Blocks in Classes](#4static-blocks-in-classes)


#1.Control Flow Analysis of Aliased Conditions and Discriminants

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

v4.3以前

```Javascript
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`と組み合わせれば便利な気がします。

```Javascript
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は機能しないので注意が必要です。

```Javascript
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()`で出力されないので注意

```Javascript
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が特定のパターンで作成されている場合などに便利

```Javascript
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の型定義が可能となり、今までよりも型安全性が保たれます。

```Javascript
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時点）


```Javascript
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/