Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

TypeScript学習日記 その0012 とてもやさしいMapped Types入門

参考にさせていただいたページ

とても参考になる入門と初級の記事です。

はじめに

以下のページを読んで、Mapped Typesが理解できる人はこの記事を読む必要は全くありません。

これらのページを読んでもMapped Typesを全く理解できないという人が、Mapped Typesを理解していく為のとっかかりをつかむ。
この記事は、そんな「超入門」記事になっています。

Mapped Typesを使うと、stringの部分型、numberの部分型、symbolの部分型、をキーとしたオブジェクトの型を定義することができます。

部分型についての前提知識

例えば 'foo' | 'bar' のようなstring literal型は、string型の部分型です。

Mapped Typesの最も簡単な例

以下のようにMyObj型を定義した場合、

    type MyObj = {[Key in 'foo' | 'bar']: number};

MyObj型は、以下のように定義したのと同じ型になります。

    type MyObj = {
        foo: number;
        bar: number;
    }

'foo' | 'bar' が順番に変数Keyに入り、MyObj型のキーになっていることがわかります。

'foo' | 'bar'の部分について

さきほどの「Mapped Typesの最も簡単な例」の in の右側の 'foo' | 'bar' の部分には、string | number | symbolの部分型が使用できます。

今回の例でいうと、stringの部分型である 'foo' | 'bar' を使用しています。

当然ですがbooleanなどは使用できません。

Genericsを使ってMyObj型を定義する

Genericsを使って、型を返す関数みたいなものが作れます。

以下のように書いても、MyObj型は「Mapped Typesの最も簡単な例」で定義したものと同じMyObj型になることがわかると思います。

    // Genericsを使ってMyMappedType型を定義(型を返す関数みたいなものを定義)
    type MyMappedType<T extends string | number | symbol> = { [Key in T]: number }

    // MyMappedType<T>を実行して、MyObj型を定義
    type MyObj = MyMappedType<'foo' | 'bar'>

さきほど説明したように、[Key in T]のTにはstring | number | symbolの部分型しか使用できないため、以下のようにMyMappedType型を定義する時点で型引数Tは string | number | symbol の部分型であることを明示しています。

MyMappedType<T extends string | number | symbol>

MyMappedTypeの型引数Tとして、string literal型を渡すことが決まっているのであれば以下のようにstringの部分型であることを明示するのが良いと思います。

MyMappedType<T extends string>

FooBar型を定義する

以下のようにFooBar型を定義しても同じMyObj型が定義できることがわかると思います。

    // FooBar型を定義
    type FooBar = 'foo' | 'bar'

    // Genericsを使ってMyMappedType型を定義(型を返す関数みたいなものを定義)
    type MyMappedType<T extends string | number | symbol> = { [Key in T]: number }

    // MyMappedType<T>を実行して、MyObj型を定義
    type MyObj = MyMappedType<FooBar>

おわりに

今回、以下のようにMyObj型のvalueの型はnumberで固定していますが、

{ [Key in T]: number }

以下のように変数Keyをオブジェクトのvalueとして使うこともできます。

{ [Key in T]: Key }

そうするとMyObj型は以下のようになります。

type MyObj = {
    foo: "foo";
    bar: "bar";
}

超入門記事なのでここまでにしますが、以下の記事を読むと、より発展的な使い方やunion distributionなどについて学ぶことができます。

少しでも誰かの理解の助けになれば幸いです。