mosya
mosya Business はこちら

mosya<TC> - 配列に要素が含まれればtrueを返す型を作ろう

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

問題

JavaScriptのArray.include関数を型システムに実装します。この型は、2つの引数を受け取り、truefalseを出力しなければなりません。

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

type isPillarMen = Includes<
  [
    "Kars",
    "Esidisi",
    "Wamuu",
    "Santana"
  ],
  "Dio"
>; // expected to be `false`

前提知識

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

  1. ジェネリクスを理解する
  2. Conditional Typesを理解する
  3. インデックスアクセス型を理解する

ジェネリクスを理解する

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

この受け取った値を使って、新しい型を生成することができます。

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

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

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

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

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

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

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

型の制約

Textendsをつけることで制約をつけることができます。

例えば、T extends stringとすると、Tstring型かstring型を継承した型に制約されます。

Conditional Typesを理解する

Conditional Typesは、条件によって型を変更することができる機能です。

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

type Foo<T> = T extends string
  ? string
  : number;

この型は、Tstring型を継承している場合はstring型を、そうでない場合はnumber型を返します。

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

type FooString = Foo<string>; // string
type FooNumber = Foo<boolean>; // number

このようにConditional Typesは、条件によって型を変更することができます。

インデックスアクセス型を理解する

インデックスアクセス型は、オブジェクトのプロパティにアクセスするための機能です。

例えば、以下のようなオブジェクトがあったとします。

interface Todo {
  title: string;
  description: string;
}

このオブジェクトのプロパティにアクセスするには、以下のようにします。

type Title = Todo["title"]; // string

このように、Todo["title"]とすることで、Todotitleプロパティの型を取得することができます。
これをインデックスアクセス型と呼びます。

このインデックスアクセス型は配列にも使うことができます。

type Cars = [
  "Toyota",
  "Honda",
  "Nissan"
];
type Car = Cars[0]; // "Toyota"

また、numberを使うことで、配列の要素の型をユニオン型として取得することができます。

type Cars = [
  "Toyota",
  "Honda",
  "Nissan"
];
type Car = Cars[number]; // "Toyota" | "Honda" | "Nissan"

解答例

以上の前提知識を踏まえて、以下のように解答することができます。

type Includes<
  T extends readonly any[],
  U
> = U extends T[number] ? true : false;

この解答例では、T extends readonly any[]という制約をつけています。
これにより、Tは配列であり、変更不可能な配列であることが保証されます。

T[number]により配列の値がユニオン型として取得されます。
よって、UT[number]のどれかに当てはまる場合はtrueを、そうでない場合はfalseを返すことになります。

Authored by

筆者の写真

Godai@steelydylan

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

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

詳しくはこちら
mosya

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

© 2023 - mosya. All rights reserved.