[Typescript]Object.keysの型がstringになってしまう問題の対処法

Typescript

Object.keysでdictからkeyを取り出そうとしたときに、stringになってしまう問題の対処法

export type Drinks = "beer" | "wine" | "cocktail";

const drinkPrices: { [key in Drinks]: number } = {
  beer: 500,
  wine: 800,
  cocktail: 600,
};

const drinkQuantities: { [key in Drinks]: number } = {
  beer: 4,
  wine: 2,
  cocktail: 1,
};

const drinkList = Object.keys(drinkPrices).map((key) => {
  return {
    name: key,
    // ! No index signature with a parameter of type 'string' was found on type '{ beer: number; wine: number; cocktail: number; }'.
    price: drinkPrices[key],
    // ! No index signature with a parameter of type 'string' was found on type '{ beer: number; wine: number; cocktail: number; }'.
    quantity: drinkQuantities[key],
  };
});

対処法1. Object.keysの型を上書きする

src/types/ObjectKeys.d.tsなどを作成し、以下のように型を上書きします

type ObjectKeys<T> = T extends { [key: string]: unknown } ? (keyof T)[] : never;

interface ObjectConstructor {
  keys<T>(o: T): ObjectKeys<T>;
}

これでObject.keysを使用する場合は自動的に推論が当たるようになります
既存の書き方を変えずに型だけ変えられるので、一番おすすめのやり方になります。


対処法2. util関数を作成する

Object.keysのラッパー関数をutilityとして作成し、Object.keysの代わりに使用する方法です

const objectKeysWithTypes = <T extends { [key: string]: unknown }>(
  obj: T
): (keyof T)[] => {
  return Object.keys(obj);
};

暗黙のうちに型解決を行う1に比べて、型推論を行っていることを明示できるというメリットはありますが、好みの範疇かなと思います。

対処法3. asで注釈をつける

asを用いてObject.keysの結果に無理やり型を付ける方法です

export type Drinks = "beer" | "wine" | "cocktail";

const drinkPrices: { [key in Drinks]: number } = {
  beer: 500,
  wine: 800,
  cocktail: 600,
};

const drinkQuantities: { [key in Drinks]: number } = {
  beer: 4,
  wine: 2,
  cocktail: 1,
};

const drinkList = (Object.keys(drinkPrices)  as Drinks[]).map((key) => {
  return {
    name: key,
    price: drinkPrices[key],
    quantity: drinkQuantities[key],
  };
});

Object.keysを用いるたびにas句を使わないといけない上に、Object.keysの引数の型が変わったことを検知できないため、変更に弱くなります。
納期に追われている時以外は使用しない方がいいです

コメント

タイトルとURLをコピーしました