TypeScript の小ネタ。
TypeScript で再帰的なデータ構造のデータに対して型を適用する方法です。
例えば、クローリングしてきた Web サイトの情報など。JSON の key があって、値がstring | number | boolean | Array | Object
になる可能性がある面倒なやつです。
普通に考えると、[key: string]
の Index signature と Union Type を持つ型を使えばいいかと思うのですが、肝心の Union の型定義が再帰構造なので表現できません。
interface WebSiteData {
// Unionの部分が再帰的なので表現できない!!
[key: string]: string | number | boolean | Array<
string | number | boolean | Array<
string| number | boolean | Array<
...え、ちょっと待って
>
>
>
}
まずこの問題を解決するには、JSON の値が取る型定義を最初にイメージします。名前をJSONValueType
として、後で定義する Array や Object はこの型構造を使うことにします。
JSONValueType
は値がstring | number | boolean | Array | Object
になる可能性があるので、型定義はだいたい次のようなものになると考えます。
// Array<JSONValueType> | Object<JSONValueType>はあとで置き換えます。
type JSONValueType =
| string
| number
| boolean
| Array<JSONValueType>
| Object<JSONValueType>;
次にArray<JSONValueType>
の型定義をします。名前はJSONValueTypeArray
にしましょう。
定義するにはJSONValueType
型の Array を継承した interface を作成します(ちょっとトリッキーです)。定義した後は、JSONValueType
の型定義も置き換えます。
type JSONValueType =
| string
| number
| boolean
| JSONValueTypeArray
| Object<JSONValueType>;
interface JSONValueTypeArray extends Array<JSONValueType> {}
次にObject<JSONValueType>
の型定義をします。名前はJSONValueTypeObject
にしましょう。
こちらも定義するには[key: string]
の Index signature を持ってJSONValueType
の値を持つ interface を定義します。
定義した後は、JSONValueType
の型定義も置き換えます。
type JSONValueType =
| string
| number
| boolean
| JSONValueTypeArray
| JSONValueTypeObject;
...
interface JSONValueTypeObject {
[key: string] : JSONValueType;
}
これで不定形の JSON データ構造をJSONValueTypeObject
を使って型安全に定義することができました。
const data: JSONValueTypeObject = {
name: "mitsuruog",
age: 20,
alive: true,
profile: [
{
profileName: "aaa",
profileNumber: 1,
profileBool: true,
profileArray: [],
profileObject: {},
},
],
address: {
addressString: "aaa",
addressNumber: 1,
addressBool: true,
addressArray: [],
addressObject: {},
},
};
参考リンク
もう少し簡単に定義できるようにしたかったみたいですが、実現されなかったみたいですね。。。