お題
タプル型T
にU
が含まれているか判定する型Includes
を実装します。JavaScriptのArray.include
関数を型システムに実装します。
やりたいこと
type test1 = Includes<["Kars", "Esidisi", "Wamuu"], "Kars">; // true
type test2 = Includes<["Kars", "Esidisi", "Wamuu"], "Dio">; // fasle
type test3 = Includes<[{}], { a: "A" }>; // false
type test4 = Includes<[boolean, 2, 3, 5, 7], false>; //false
解答
type Includes<T extends readonly any[], U> = {
[P in T[number]: true;
}[U] extends true
? true
: false;
解説
処理の流れ
-
{ [P in T[number]: true; }
T[number]
で返されたユニオン型を使って、オブジェクトを作成する。
example.ts
// Tが["Kars", "Esidisi", "Wamuu"]場合
// ["Kars", "Esidisi", "Wamuu"][number] => "Kars" | "Esidisi" | "Wamuu"
{
"kars": true,
"Esidisi": true,
"Wamuu": true,
}
-
{ [P in T[number]: true; }[U]
インデックスアクセス型の利用 -
{ [P in T[number]: true; }[U] extend true ? true : false
インデックスアクセス型でプロパティの型がtrue
かどうかで条件分岐を行う。
// Includes<["Kars", "Esidisi", "Wamuu"], "Kars">; の場合
{"kars": true, "Esidisi": true, "Wamuu": true,}["kars"] // => true
なぜ下記のコードでは期待した結果が得られないのか?
type TestInclude<T extends readonly any[], U> = U extends T[number]
? true
: false;
こちらのコードでは、T
にユニオン型やオブジェクトが含まれる場合、期待した結果を得られません。
type test5 = Includes<[{}], { a: "A" }>; // => falseのはずがtrue
type test6 = Includes<[true, 2, 3, 5, 6, 7], boolean>; // falseのはずがtrue
type test7 = Includes<[1], 1 | 2>; // falseのはずがtrue
単純なextends
チェックの場合、ユニオン型では部分一致が、オブジェクトやboolean型ではサブタイプが許容される。そのため、期待した結果が得られない。
[number]とは...
インデックスアクセス型とは...
参考記事
今回の問題