かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

TypeScript Object を Object.keys() を使ったループの型エラーにハマる

TypeScript で Object.keys(obj) でキーの配列を作ってループさせてループ内でキーでオブジェクトのデータにアクセスしようとして型エラーになってしまったのメモ

e.g.

例としてデータ構造が良くないけど、ID をキーにしたオブジェクトみたいなイメージで

const idols = {
  hosimiya: {name: '星宮いちご', type: 'cute', brand: 'Angely Sugar'},
  kirija: {name: '霧矢あおい', type: 'cool', brand: 'FUTURING GIRL'},
  shibuki: {name: '紫吹蘭', type: 'sexy', brand: 'SPICY AGEHA'},
};

Object.keys(idols).map((key) => {
  const data = idols[key];
  // => Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ hosimiya: …
  // => No index signature with a parameter of type 'string' was found on type '{ hosimiya: …
  console.log(data);
});
  • Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ hosimiya: …
  • No index signature with a parameter of type 'string' was found on type '{ hosimiya: …

オブジェクトに [key] でアクセスすると暗黙的に型変換された keyidols オブジェクトのインデックスと一致しないということっぽい。
(ᐡ •̥ ̫ •̥ ᐡ) なんもわからん…

Object.keys で作られる配列がオブジェクトのキーの配列だと証明できればOK

keyof を使うとプロパティ名を | でつないだ型 ( プロパティ名の直和型 ) が作れる

interface Soleil {
  'hosimiya': { name: string, type: string, brand: 'string' };
  'kirija': { name: string, type: string, brand: 'string' };
  'shibuki': { name: string, type: string, brand: 'string' }; 
}

type SoleilKey = keyof Soleil;
// => type SoleilKey = 'hosimiya' | 'kirija' | 'shibuki';

Object.keys(idols)SoleilKey[] になっていれば、map に渡される引数は SoleilKey である。ということになるっぽい。
修正するとこんな感じ。

interface idolData {
  name: string;
  type: string;
  brand: string;
}

interface Soleil {
  hosimiya: idolData;
  kirija: idolData;
  shibuki: idolData;
}

const idols: Soleil = {
  hosimiya: {name: '星宮いちご', type: 'cute', brand: 'Angely Sugar'},
  kirija: {name: '霧矢あおい', type: 'cool', brand: 'FUTURING GIRL'},
  shibuki: {name: '紫吹蘭', type: 'sexy', brand: 'SPICY AGEHA'},
};

(Object.keys(idols) as (keyof Soleil)[]).map((key) => {
  const data = idols[key]; // 👌👌👌
  console.log(data);
});

₍ ᐢ. ̫ .ᐢ ₎👌 できた

おまけ

今回のレベルだと [key: string] で型定義してしまえば、map の所でゴニョゴニョしなくても大丈夫っぽい

interface idolData {
  name: string;
  type: string;
  brand: string;
}

interface Idols {
  [key: string]: idolData;
}

const idols: Idols = {
  kurebayashi: {name: '紅林珠璃', type: 'sexy', brand: 'Sangria Rosa'},
  Amahane: {name: '天羽まどか', type: 'cute', brand: 'Angely Sugar'},
  Kurosawa: {name: '黒沢凛', type: 'cool', brand: 'Dance Fusion'},
};

Object.keys(idols).map((key) => {
  const data = idols[key]; // 👌👌👌
  console.log(data);
});

TypeScript チョットづつ慣れてきてるけどまだ何もわからん…


[参考]

速習 TypeScript 第2版 速習シリーズ

速習 TypeScript 第2版 速習シリーズ