⚡️

TypeScript 5.5からは関数からType predicatesの型推論が有効になるよ!

2024/04/28に公開

去る2024年4月25日にTypeScript 5.5 ベータ版リリースの情報が発表されました。

https://devblogs.microsoft.com/typescript/announcing-typescript-5-5-beta

どうやら今回の目玉機能は、『推論されたtype predicate』ということです。

この記事では、これまでとこれからでtype predicateがどのように変わるのかをお話ししたいと思います。

環境の用意

これまでの動作を確認するための環境は、既に用意していた別プロジェクトのランタイムを利用しました。バージョンは5.1.6です。

ベータ版環境は新たに用意します。公式のリリースノートにもありますが、以下のコマンドを実行するだけです。

npm install -D typescript@beta

これでベータ版の実行環境ができたのですが、VSCodeさんが最新版の仕様で型推論を行なってくれません。

ので、調教強制的にいうことを聞かせます。

やり方は、適当なtsファイル開いてshift + cmd + ptypescript:Select Typescript Versionから正しいバージョンを選択します。

上記のように、選択肢が表示されるため、5.5.0-betaを選択すれば準備OKです。

そもそも、Type predicateって?

これは名前に馴染みがない方でも、普段コードを書く中でナチュラルに使っている方も多いハズ。

例えば以下のコード。

function isCar(car: Car | undefined): car is Car {
  return car !== undefined;
}

このような型ガードを書くことでundefinedを間引き、安全にCarとして扱うことができます。

このcar is Carの部分がType predicateと呼ばれるものです。

改めて、5.5以前のコードを確認する

Type predicateが何かわかったところで、以下のコードを見てください。

interface Car {
  maker: string;
  carNumber: number;
  run(): void;
}

declare const makerCars: Map<string, Car>;

function isCar(car: Car | undefined){
  return car !== undefined;
}

function runCar(makers: string[]) {
  const cars = makers
    .map(maker => makerCars.get(maker))
    .filter(car => isCar(car));

  // なぜか型がCar | undefinedになってしまう
  for (const car of cars) {
    car.run();
  }
}

先ほどの話を踏まえるのであれば、isCarの戻り値は明記していないものの型推論が働き、.filterによってCar型のオブジェクトのみに絞られるハズです。

その後car.run()は問題なく実行できそうですが…

carundefinedと推定されてしまっています。なぜこのようなことが起こるのでしょうか?

5.5以前はType predicateを型推論してくれない

では、TypeScriptは何を推論しているのか確認してみると…

どうやら愚直にbooleanと推論してしまっているようです。間違ってはいませんが、文脈からは少し外れてしまているようにも思えますね。

5.5からはType predicateを型推論してくれる

では、先ほどと同じコードを5.5の実行環境で確認してみると…

エラーが消えました!そしてisCarの型推論はというと…

素晴らしい👏 car is Carが推論されていますね👏

おわりに

詳細はこの機能の実装者であるDan Vanderkamさんのブログに記載されています。

対応にあたってのもろもろの経緯が書かれていて、とても面白いです。

https://effectivetypescript.com/2024/04/16/inferring-a-type-predicate/

Discussion