TypeScript 5.5からは関数からType predicatesの型推論が有効になるよ!
去る2024年4月25日にTypeScript 5.5 ベータ版リリースの情報が発表されました。
どうやら今回の目玉機能は、『推論されたtype predicate』ということです。
この記事では、これまでとこれからでtype predicateがどのように変わるのかをお話ししたいと思います。
環境の用意
これまでの動作を確認するための環境は、既に用意していた別プロジェクトのランタイムを利用しました。バージョンは5.1.6です。
ベータ版環境は新たに用意します。公式のリリースノートにもありますが、以下のコマンドを実行するだけです。
npm install -D typescript@beta
これでベータ版の実行環境ができたのですが、VSCodeさんが最新版の仕様で型推論を行なってくれません。
ので、調教強制的にいうことを聞かせます。
やり方は、適当なtsファイル開いてshift + cmd + p → typescript: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()は問題なく実行できそうですが…

carはundefinedと推定されてしまっています。なぜこのようなことが起こるのでしょうか?
5.5以前はType predicateを型推論してくれない
では、TypeScriptは何を推論しているのか確認してみると…

どうやら愚直にbooleanと推論してしまっているようです。間違ってはいませんが、文脈からは少し外れてしまているようにも思えますね。
5.5からはType predicateを型推論してくれる
では、先ほどと同じコードを5.5の実行環境で確認してみると…

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

素晴らしい👏 car is Carが推論されていますね👏
おわりに
詳細はこの機能の実装者であるDan Vanderkamさんのブログに記載されています。
対応にあたってのもろもろの経緯が書かれていて、とても面白いです。
Discussion