ProgrammingTypeScriptアーキテクト

TypeScriptにおける条件付き型(conditional types)はどのように機能しますか?条件付き型の分配的な振る舞いとは何ですか?例を挙げて説明してください。

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

答え。

TypeScriptの条件付き型(conditional types)は、T extends U ? X : Yの原則に従って、一つの型を別の型を使って記述することを可能にします。これらの型は、特にライブラリやAPIの宣言を構築する際に、複雑な型のロジックを構築するための強力な機能を提供します。

基本的な条件付き型の例:

type IsString<T> = T extends string ? 'yes' : 'no'; type A = IsString<string>; // 'yes' type B = IsString<number>; // 'no'

分配的な振る舞い

条件付き型がunion型に適用されると、TypeScriptは条件をunionの各要素に対して「配分」します。これが条件付き型の分配的な振る舞いと呼ばれます。

例:

type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean

これは非常に強力な機能ですが、特に配列や型のマッピングを扱う際に、期待される結果との間に混乱を引き起こす可能性があります。

分配的な振る舞いを避ける方法

型をタプルでラップします:

type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean

ひねりのある質問。

質問: 「タプルでラップせずにunion型と共に条件付き型を使用した場合、何が起こりますか?結果は常に通常のロジックと同じですか?」

答え: 結果は予期しないものになることがあります!分配性のため、条件がunion型の各メンバーに対して個別に適用されます。全体のunion型を厳密に比較するにはラップ(タプル)を使用する必要があります。

例:

type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean、ただのbooleanではない

このテーマの微妙な点を知らないための実際のエラーの例。


物語

シリアライズのライブラリでデータ構造をチェックするために条件付き型を使用しましたが、ジェネリックパラメータをタプルでラップするのを忘れました。その結果、複雑なunion型で宣言が壊れ、コンパイラがAPI使用時に予測不可能な型を出力しました。


物語

モデルのフィールドを処理するための型変換を実装しようとしたとき、一部の情報が失われました。分配性がunionを引き起こすため、いくつかのロジックの枝を処理し忘れ、その結果、型付けが非常に許容的になりました。


物語

開発者はT extends SomeTypeが条件付き型の内部で全体のオブジェクトに対して期待通りに機能すると思っていましたが、「散逸」が発生しました。コンパイラは不整合を指摘し、タイプカオスや自動ドキュメント生成時の深刻なバグが発生しました。