ProgrammingTypeScript/フルスタック開発者

TypeScript における infer を用いた条件付き型はどのように機能し、どのような状況で必要とされ、実際にどのようなエラーが発生することがありますか?

Hintsage AIアシスタントで面接を突破

回答。

条件付き型は infer キーワードを使用することで、複雑なデータ型から型を抽出することを可能にします。代表的な例は、配列から要素の型を抽出することです。

type ElementType<T> = T extends (infer U)[] ? U : T;

ここで infer U は、配列 T の要素の型を計算することを可能にします。もし T が配列であれば、その要素の型が返され、それ以外の場合は T 自体が返されます。

使用例:

  • 汎用的なヘルパー型の作成(例えば、関数から返り値の型を抽出する、Promise の値の型など)。

例:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

ひっかけ質問。

一つの条件付き型内で複数の infer を使用することはできますか?TypeScript はこのケースをどのように解釈しますか?

間違った答え:

  • "一つの infer のみ使用でき、TypeScript はそれ以上を許可しません。"

正しい答え:

  • 一つのテンプレート内で複数の infer 変数を使用することができます。例えば、タプルを解析する場合:
type FirstArgument<T> = T extends (infer F, ...any[]) => any ? F : never; // しかし関数と一緒に使う方が正確です: type Args<T> = T extends (...args: infer A) => any ? A : never;
  • TypeScript は異なる infer 変数を介して複数の型を正しく推論します。

このトピックの細かい点を知らなかったことによる実際のエラーの例。


逸話

開発者は関数が返すオブジェクトの型を取得する方法を記述しましたが、その関数が Promise を返す可能性を考慮しませんでした。結果として、返り値の型は常に Promise<any> となり、入れ子の conditional extract/infer を使用しなかったため、コード全体のリファクタリングが必要になりました。


逸話

プロジェクトでネストされた配列を展開するための汎用型を導入しましたが、条件に最終的な else-branch を指定するのを忘れました。TypeScript は正しくエラーを出しませんでしたが、一部のケースでは結果が never となり、外部ライブラリのいくつかのユーティリティ型の動作が壊れてしまいました。


逸話

同僚は大きなインターフェースからプロパティの型を抽出するために combine-conditional/infer-型を試みましたが、いくつかのプロパティが自身が union 型であることを考慮しませんでした。結果として、出力として予期しない union 型の組み合わせが生成され、コンパイラはそれを通過し、ロジックが正しく機能しませんでした。