TypeScriptでObjectからデータを取得する時、ブラケット記法で書くと強制的に型がanyになって、大変辛い思いをしたので、以下に残しておく。
Objectのサンプルは以下の通り。ポイントは、exampleAB.a
以降とexampleAB.b
以降とで型が違うこと。
interface ExampleAB { 'a': { a_example: { example: string } }, 'b': { b_example: { example: number } } } const exampleAB: ExampleAB = { 'a': { a_example: { example: 'dummy' } }, 'b': { b_example: { example: 1 } } }
たとえば、以下のようにするとObjectからデータを取得することができる。
当たり前ですね。
console.log(exampleAB.a.a_example.example) // 'dummy'
しかし、これをブラケット記法で書くとTypeScript側で型を判断できずにエラーになる。
( T 0 T ) ォォォォ...
const k = 'a' console.log(exampleAB[k as strng].a_example.example) // 型 'any' の式を使用して型 'ExampleAB' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。
keyof
を使えばワンチャンあるかも・・・と思い、試すもダメだった。
const k = 'a' console.log(exampleAB[k as keyof ExampleAB].a_example.example) // プロパティ 'a_example' は型 '{ a_example: { example: string; }; } | { b_example: { example: number; }; }' に存在しません。 プロパティ 'a_example' は型 '{ b_example: { example: number; }; }' に存在しません。
どうやら、Objectの構造が深いとダメな模様。
Objectのデータ構造が、'exampleAB.a'と'exampleAB.b'までであれば、TypeScriptが判断してくれることを確認した。
interface ExampleAB2 { 'a': string 'b': number } const exampleAB2: ExampleAB2 = { 'a': 'dummy1', 'b': 1 } const i = 'a' console.log(exampleAB2[i as keyof ExampleAB2]) // 'dummy'
もうすこし、根気よくやってみよう。
「そうだ!interfaceを分けてみよう!」と思い、試してみた結果がこちら。。。
interface ExampleA { 'a': { a_example: { example: string } } } interface ExampleB { 'b': { b_example: { example: number } } } const exampleAB: (ExampleA | ExampleB) = { 'a': { a_example: { example: 'dummy' } }, 'b': { b_example: { example: 1 } } } const k = 'a' const i = 'b' console.log(exampleAB[k as keyof ExampleA].a_example.example) // 型 '"a"' の式を使用して型 'ExampleA | ExampleB' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。 プロパティ 'a' は型 'ExampleA | ExampleB' に存在しません。 console.log(exampleAB[i as keyof ExampleB].b_example.example) // 型 '"b"' の式を使用して型 'ExampleA | ExampleB' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。 プロパティ 'b' は型 'ExampleA | ExampleB' に存在しません。
orz... ダメだ...
ならば・・・と、exampleAB
に対して型アサーションを定義してあげればうまくいきました。
const k = 'a' const i = 'b' console.log((exampleAB as ExampleA)[k as keyof ExampleA].a_example.example) // 'dummy' console.log((exampleAB as ExampleB)[i as keyof ExampleB].b_example.example) // 1
まさかブラケット記法で書くと型が強制的にanyになってしまうとは。。。。
TypeScriptで型を定義するのは、なかなか慣れないものですね。
最近は、QiitaでTypeScriptの型定義の演習問題を作ってくれている方がいて、こちらを使って修行しています。
とはいえ、なかなか問題が難しくて軽々解けない。
これが軽々解けるようになると、大幅にレベルアップできるかもしれない。。。そんな事を思いながら日々修行を続けていますm( = = )m