課題
タイトルの通り、下記のような交差(配列)型 HogePiyo
に対して array.map でオブジェクトを取り出した場合、そのオブジェクト内に存在するはずのプロパティーが型情報として欠けてしまいます。これは TypeScript PlayGround でも確認する事ができます。
type Hoge = { hoge: number }[];
type Piyo = { piyo: string }[];
type HogePiyo = Hoge & Piyo;
const arrayHogePiyo: HogePiyo = [
{
hoge: 1,
piyo: "a"
}
];
// OK
const piyo = arrayHogePiyo[0].piyo;
// NG: Property 'piyo' does not exist on type '{ hoge: number; }'.(2339)
const arrayPiyo = arrayHogePiyo.map((item) => item.piyo)
解決方法
ものすごく単純ですが HogePiyo
の型定義を下記のように変更します。
type Hoge = { hoge: number };
type Piyo = { piyo: string };
type HogePiyo = (Hoge & Piyo)[];
const arrayHogePiyo: HogePiyo = [
{
hoge: 1,
piyo: "a"
}
];
// OK
const arrayPiyo = arrayHogePiyo.map((item) => item.piyo)
結論
配列型同士を交差型にするのではなく、配列要素(オブジェクト)同士を交差型にして配列型を定義してください。この問題に対しては TypeScript - issues11961 でも取り上げられています。
// BAD(推論の結果が不透明であり、悪い定義)
type InterSectionArray = Hoge[] & Piyo[];
// GOOD
type InterSectionArray = (Hoge & Piyo)[];
余談(経緯)
お仕事で GraphQL Code Generator を使用しており、そいつが自動で生成してくれる型定義において、上記のような交差(配列)型と array.map 問題に当たってしまったので記事として残しました。自動生成による型定義の場合どうしようもないので、一旦下記のように解決しました。
// 自動生成された型
type BaseConnection = {
edges: BaseEdge[]
}
type HogeConnection = {
edges: HogeEdge[]
}
type Connection = BaseConnection & HogeConnection;
// 解決前: connection.edges の型が BaseEdge[] & HogeEdge[] なため array.map での推論に失敗
const idList = connection.edges.map((item) => item.id);
// 解決: as 句を使って (BaseEdge & HogeEdge)[] に cast する
const idList = (connection.edges as (BaseEdge & HogeEdge)[]).map((item) => item.id);