APIからのレスポンスをtype
で定義した際に、以下のようなルールに従って記載したところ掲題の内容にハマりました。
- レスポンスには「詳細」と「一覧」の2通りがある
- 「詳細」は
data
に任意のオブジェクトインスタンスが入る - 「一覧」は
data
に、データ件数を表すcount
と任意のオブジェクトインスタンスの配列であるlist
が入る
イメージとしては以下の通りです。
typescript
1// 一覧のレスポンス 2const res1 = { 3 data: { 4 count: 2, 5 list: [ 6 {aaa: "1"}, 7 {aaa: "2"} 8 ] 9 } 10} 11 12// 詳細のレスポンス 13const res2 = { 14 data: { 15 aaa: "2" 16 } 17}
まず「一覧」のレスポンスの型としてListRes
を定義し、list
の型はジェネリクスで受け取るようにしています。
typescript
1type ListRes<T> = { 2 count: number; 3 list: T[]; 4}
さらに両種のレスポンスを包括するものとしてResBase
を定義しました。
data
はT
型そのものかListRes<T>
型になります。
typescript
1type ResBase<T> = { 2 data: T | ListRes<T> 3}
このResBase
で実際にインスタンスを作成した際に「詳細」か「配列」が特定できているにもかかわらず型推論が効かないことがわかりました。
typescript
1const sample1: ResBase<{aaa: string}> = { 2 data : { 3 count: 1, 4 list: [ 5 {aaa: "1"}, 6 ] 7 } 8} 9 10// 「配列」のレスポンスであると特定できていそうなのに型推論が効かない 11sample1.data.list;
以下のような構文エラーが出ます。
プロパティ 'list' は型 '{ aaa: string; } | ListRes<{ aaa: string; }>' に存在しません。 プロパティ 'list' は型 '{ aaa: string; }' に存在しません。ts(2339)
あれこれ試してみると、ResBase
の定義を以下のように見直すと型推論が効くようになることが分かりました。
typescript
1// type ResBase<T> = { 2// data: T | ListRes<T> 3// } 4 5// data内でなくResBaseそのものをユニオン型にする 6type ResBase<T> = { 7 data: T 8} | { 9 data: ListRes3<T> 10}
dataをユニオン型にするか、ResBaseそのものをユニオン型にするかの違いで型推論ができるようになるのがイマイチ理解できません。
これはTS
の仕様で、「そういうものか」と納得するしかないのでしょうか?
あなたの回答
tips
プレビュー