Apr 7, 2022
6 mins read
前回のmattさんのTypeScript Tip #15が勉強になったので、最初から#1からまとめていく
export const fruitCounts = {
apple: 1,
pear: 4,
banana: 26
}
上記の様なコードがあった時に、特定のキーを取得する型エイリアスで定義すると以下になる。
type SingleFruitCount =
| {
apple: number
}
| {
banana: number
}
| {
pear: number
}
const singleFruitCount: SingleFruitCount = {
apple: 10,
}
SingleFruitCount
の型エイリアスを作成すれば、型定義できるが定数fruitCounts
を型推論させて型エイリアスを作成する。
最終的なコードは以下になる。具体的に解説していく。
type FruitCounts = typeof fruitCounts
type NewSingleFruitCount = {
[ K in keyof FruitCounts ]: {
[ K2 in K ]: number
}
}[keyof FruitCounts]
// type NewSingleFruitCount = {
// apple: number;
// } | {
// pear: number;
// } | {
// banana: number;
// }
typeof fruitCounts
は型推論が効いて以下の型が作成される。type FruitCounts = {
apple: number;
pear: number;
banana: number;
}
[ K in keyof FruitCounts ]
のkeyof FruitCounts
は型エイリアスのオブジェクトのキーのみを取得することができる。keyof FruitCounts
は"apple" | "pear" | "banana"
変換される。[ K in keyof FruitCounts ]
は[ K in "apple" | "pear" | "banana" ]
に変換し、マップ型で型を繰り返し実装される。[K2 in K]: number
でK
は単独のリテラル型( "apple" | "pear" | "banana
)になる。なので、K2
もK
と同じリテラル型になる。type NewSingleFruitCount = {
apple: {
apple: number;
};
pear: {
pear: number;
};
banana: {
banana: number;
};
}
なぜ新たに[K2 in K]
マップ型を作成しているかというと、もしK: number
で実装した場合、
type NewSingleFruitCount = {
apple: {
K: number;
};
pear: {
K: number;
};
banana: {
K: number;
};
}
K
のリテラル型になり、マップした変数K
が使用できなくなる。オブジェクトのキーで変数を使用したい場合は、インデックスシグネチャを使用するしかなくなる。
[keyof FruitCounts]
は["apple" | "pear" | "banana"]
になり、ルックアップ型でキーを指定して取得することできる。ルックアップ型がユニオン型になっているため、取得した型もユニオン型で生成することができる。type NewSingleFruitCount = {
apple: {
apple: number;
}
pear: {
pear: number;
}
banana: {
banana: number;
}
}["apple" | "pear" | "banana"]
になり、以下のコードになる
type NewSingleFruitCount = {
apple: number;
} | {
pear: number;
} | {
banana: number;
}
TypeScriptにはユーティリティ型が充実しているため、マップ型やルックアップ型を使うことが少ないが、いつでも使える状態にしておきたい。