mosya
mosya Business はこちら

mosya<TC> - 配列からオブジェクトを生成する型を作ろうの解説

この記事はmosya<TC>の問題の一つであるTupleToObject型の解説になります。

問題

タプルを受け取り、その各値のkey/valueを持つオブジェクトの型に変換する型を実装します。

例えば以下のようなコードを満たすように実装しましょう。

const tuple = [
  "tesla",
  "model 3",
  "model X",
  "model Y",
] as const;

type result = TupleToObject<
  typeof tuple
>; // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

前提知識

この問題を解くにあたって型についての以下の知識を理解しておく必要があります。

  1. ジェネリクスを理解する
  2. readonly修飾子による制約
  3. Mapped Typesを理解する

ジェネリクスを理解する

まず、<T>というのは、ジェネリクスと呼ばれる機能です。ジェネリクスは、型をパラメータとして受け取ることができる機能です。

この受け取った値を使って、新しい型を生成することができます。
受け取った型を活用して、新しい型を生成することができるので、型の再利用性が高くなります。

例えば、以下のような型が考えられます

type Foo<T> = {
  bar: T;
};

この型は、Tという型を受け取り、barというプロパティにTを代入する型です。

この型を使うと、以下のように型を指定することができます。

type FooString = Foo<string>; // { bar: string }
type FooNumber = Foo<number>; // { bar: number }

このようにジェネリクスは型を引数として受け取って、新しい型を生成することができます。

readonly修飾子による制約

readonly修飾子は、プロパティの再割り当てを禁止する修飾子です。extendsで制約をかけることで、配列の中の値が再割り当てできないことが前提となるのでas constを指定した配列を受け取れるようになります。

type Foo<T extends readonly any[]> = {
  bar: T;
};

const car = [
  "tesla",
  "model 3",
  "model X",
  "model Y",
] as const;
// as constを使うことで、配列の中の値が再割り当てできないことが前提となる
type FooString = Foo<typeof Car>;

この場合、型は以下のようになります。

type FooString = {
  bar: readonly [
    "tesla",
    "model 3",
    "model X",
    "model Y"
  ];
};

このように、barの中の値がas constの制約を受けている場合、barの中の値は単純なstring[]ではなく、数字や文字などの具体的な値が入った配列になります。

Mapped Typesを理解する

Mapped Typesはユニオン型を使って、新しいオブジェクトの型を生成する機能です。

例えば、以下のようなユニオン型があったとします。

type TodoKeys = "title" | "description";

このユニオン型を使って、以下のようなオブジェクトの型を生成することができます。

type Todo = {
  [K in TodoKeys]: string;
};

// 以下のように展開される
type Todo = {
  title: string;
  description: string;
};

解説

Mapped Typesには文字列だけでなく数字の型も使うことができます。
例えば、以下のような配列があったとします。

const tuple = [
  "tesla",
  "model 3",
  "model X",
  "model Y",
] as const;

この配列を使って、以下のように配列の値をユニオン型として取得することができます。

type Cars = (typeof tuple)[number];
// 'tesla' | 'model 3' | 'model X' | 'model Y'

さらにこの型からオブジェクト型を生成することができます。

type Cars = (typeof tuple)[number];
// 'tesla' | 'model 3' | 'model X' | 'model Y'
type CarObject = {
  [K in Cars]: K;
};

以上をまとめると最終的に以下のようになります。

const tuple = [
  "tesla",
  "model 3",
  "model X",
  "model Y",
] as const;

type TupleToObject<
  T extends readonly any[]
> = {
  [K in T[number]]: K;
};

Authored by

筆者の写真

Godai@steelydylan

Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。

ReactやTypeScriptなどの周辺技術が学べる
オンライン学習サービスを作りました!

詳しくはこちら
mosya

mosyaはオンラインでHTML,CSS,JavaScriptを基本から学習できるサービスです。現役エンジニアが作成した豊富なカリキュラムに沿って学習を進めましょう。

© 2023 - mosya. All rights reserved.