thumbnail

TypeScript4.9~4.6の新機能まとめ

TypeScript4.9からTypeScript4.6に遡って新機能をまとめています。4.5以前の内容は別記事にまとめています。まとめてある内容は文法レベルの新機能で、細かい改良及び仕様変更についてはまとめていません。ざっと新機能だけを確認するのが目的です。記事の後ろに行けば行くほど古いバージョンについて書いているため、知っているバージョンまでスクロールする必要はありません。各見出しの後には、公式ドキュメント(英語)に対応する項目を記載しております。

TypeScript 5.xについて

別記事でまとめております。

TypeScript 4.9

satisfies演算子によるオブジェクトの型推論の改善

項目名:『The satisfies operator』

satisfied演算子というものが導入されました。
これを用いると、オブジェクトの『プロパティのスペルミスのチェック』と『プロパティの型推論』が同時にスマートに行われます。

以下のように、Color型をキーに、string | [number, number, number]型を値にもつオブジェクトpaletteを考えます。

type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    bleu: [0, 0, 255]
//  ~~~~ 1.blueと打ちたかったけどタイプミスした
//  型アノテーションしてないのでエラーは出ない
}
// 2. 以下の時点でredはnumber[]型と推論される
palette.red

型のアノテーションをしてないので、
①bleuとタイプミスしたプロパティにエラーは出ませんし、
②paltteは型推論により

{
    red: number[];
    green: string;
    bleu: number[];
}

となるので、それぞれのプロパティ値が絞り込まれています。

①のプロパティのスペルミスをなくすために次のようにアノテーションをすると、今度は②の型推論でプロパティの型が絞り込まれなくなります。

type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette: Record<Colors, string | RGB> = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: "hoge"
    // bleu: [0, 0, 255]
//  ~~~~ 1.blueというタイプミスは検出される
};
// 2. 以下はstring | RGBとなり型が絞り込まれない
// ので型推論が聞かない
palette.blue

型のアノテーションをしたので、
①bleuとタイプミスしたプロパティにエラー検出でき、
②paltteはstring | RGB型となる

よって、オブジェクトのプロパティにアクセスしても型が絞り込まれません。

これらの問題を解決するのが『satisfied演算子』です。
以下のように使います。

const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255]
//  ~~~~ 1.タイプミスは検出される
} satisfies Record<Colors, string | RGB>
//2.型の絞り込みも行われる
// [number, number, number]型になる
palette.red

オブジェクトのキーはsatisfiesで指定した型を最低限満たす必要があるので、
①Colors型にないキーはエラーとなります。
また、②型の絞り込みもスマートに行われています。

他の例

const foo = {
    hoge: "hogehoge",
    fuga: "fugafuga",
    piyo: "piyopiyo",
    // bar: 4 //エラー
} satisfies Record<string, string>
// 上のオブジェクトはRecord<string, string>を満たしたうえで次のような型になる
{
    hoge: string
    fuga: string,
    piyo: string
}

クラスのオートアクセッサー

項目名:『Auto-Accessors in Classes』

ECMAScriptの次期機能であるオートアクセッサーをサポートしています。クラスのプロパティに『accessor』をつけることで、次ようにクラスを省略して記述できます。

class Person {
    accessor name: string;
    constructor(name: string) {
        this.name = name;
    }
}
// このクラスは上クラスと同じ
class Person {
    #__name: string;
    get name() {
        return this.#__name;
    }
    set name(value: string) {
        this.#__name = name;
    }
    constructor(name: string) {
        this.name = name;
    }
}

NaNを比較演算子と用いるとエラーが出るようになった

項目名:『Checks For Equality on NaN』

以下の式はすべてエラーになります。NaNに対するチェックは、Number.isNaNを用いるようにします。

NaN === NaN
NaN !== NaN
NaN === 0
NaN !== 0
let someValue = 3
someValue === NaN

TypeScript4.8

テンプレート文字列型とinferの型推論改善

項目名: 『Improved Inference for infer Types in Template String Types』

テンプレート文字列型のinfer~extendsにおいて、リテラル型がきちんと推論されるようになりました。
なお、infer句をextendsと組み合わせる構文はTypeScript4.7で追加されています。

type Num<T> = T extends `${infer U extends number}` ? U : never;
type hoge = Num<'10'> //リテラルの10型。今まではnumber型。

配列やオブジェクトのリテラルを比較するとエラー

項目名:『Errors When Comparing Object and Array Literals』

配列、オブジェクトのリテラルを比較するとエラーが出ます。

let hoge: number[] = []
if (hoge === []) {} //エラー

Javascriptでは配列やオブジェクトの比較は値ではなく参照が等しいかどうかを見ているので、値同士を比較していると勘違いした人によるバグを防止します。

TypeSciprt4.7

ブラケットで囲まれた要素へのアクセスに対する制御フロー解析

項目名:『Control-Flow Analysis for Bracketed Element Access』

オブジェクトのキーがリテラル型かシンボルである場合、要素の型の絞り込みが可能となりました。
具体例を示します。

const key = Symbol();
const numberOrString = Math.random() < 0.5 ? 42 : "hello";
const obj = {
    [key]: numberOrString,
};
if (typeof obj[key] === "string") {
    // obj[key] はstring型に絞り込まれているのでtoUpperCase()が使える。
    // 以前はstring | number型から絞り込めなかった
    let str = obj[key].toUpperCase();
}

この改善によって、コンストラクタでプロパティが初期化されていないとエラーを吐くコードが書けます。

const key = Symbol();
class C {
    [key]: string;
    constructor(str: string) {
        // [key]の初期化をしていないのでエラー
    }
    screamString() {
        return this[key].toUpperCase();
    }
}

infer~extendsでより柔軟な記述が可能に

項目名:『extends Constraints on infer Type Variables』

下記のように、infer句とextendsを組み合わせることで柔軟な型の記述が可能となりました。

type FirstIfString<T> =
    T extends [infer S extends string, ...unknown[]]
        ? S
        : never;
// string
type A = FirstIfString<[string, number, number]>;
// "hello"
type B = FirstIfString<["hello", number, number]>;
// "hello" | "world"
type C = FirstIfString<["hello" | "world", boolean]>;
// never
type D = FirstIfString<[boolean, number, string]>;

TypeScript 4.6

super()の前に処理を書いてもエラーにならない

以下のコードは4.5以前だとエラーになっていました。

declare function doSomething(): void
class Hoge {}
class Fuga extends Hoge {
    private piyo = 0
    public constructor(){
        doSomething() // 4.6より前だとエラー
        super()
    }
}

Typescript4.5 ~ 4.0について

こちらの記事でまとめております。

参考

公式ドキュメント

https://www.typescriptlang.org/docs/handbook/release-notes/overview.html