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にはユーティリティ型が充実しているため、マップ型やルックアップ型を使うことが少ないが、いつでも使える状態にしておきたい。