2026-01-15
テストケースが定期的に更新されているため記述しているコードがテストケースを通らないことがあります TypeScript Challengeで一応テストケースを全部クリアしたコードを解説付きで載せています あくまで一つのサンプルコードと捉え他にもいい書き方があればそちらを使ってください
2026-02-21
Get Return Type
実装
type MyReturnType<T extends (...args: any[]) => any> = T extends (
...args: any[]
) => infer U
? U
: never;
EasyのParametersに近い問題。戻り値の型を調べる必要がある。Tは関数として型制限をし、予測したい戻り値の部分にinferとして推測をおこない、その値を返す。
2026-02-21
Last of Array
実装
type Last<T extends any[]> = T["length"] extends 0
? never
: T extends [...any, infer U]
? U
: 0;
inferを使う実装
type Last<T extends any[]> = T extends [...any, infer L] ? L : never
配列の長さが0のときneverを返す必要があるので、lengthプロパティで長さを調べConditional Typesで条件分岐。最後の型が知りたいのでそれを調べるためにinferを使う。もしくは配列を何かしら含むときはUを返し、それ以外のときはneverを返す。
2026-02-21
Merge
実装
type Merge<T, U> = {
[K in keyof T | keyof U]: K extends keyof U
? U[K]
: K extends keyof T
? T[K]
: never;
};
二つのkeyofの値を|でつないでユニオン型が作れるかがポイント。あとはextendsのConditional Typesで条件分岐をして、テストケースでは同じキーが存在すれば上書きをすることが求められているのでUの方を優先して、それ以外であればTのキーでMapped Typesをおこなう。keyof T | keyof UはkeyOf(T & U)と等価。
2026-02-21
Flatten
実装
type Flatten<T extends any[]> = T extends [infer U, ...infer P]
? U extends any[]
? [...Flatten<U>, ...Flatten<P>]
: [U, ...Flatten<P>]
: [];
ネストされた配列に対して、すべての配列をスプレッド展開し一次元配列にする問題。extendsが多くて見づらくはあるが、おこなっていることとしては、配列の特定の値が配列なのかを調べそれであればスプレッド演算子を使って再帰処理をおこなっているだけ。
2026-02-21
Replace
実装
type Replace<
S extends string,
FROM extends string,
TO extends string,
> = FROM extends ""
? S
: S extends `${infer L}${FROM}${infer R}`
? `${L}${TO}${R}`
: S;
S・FROM・TOともにすべて文字列であるという制約をつける。置換文字が空白の場合は文字をそのまま返し、そうじゃない場合に処理をおこなう。inferで左の文字と右の文字を推測させ、その間に置換対象文字が入っていれば処理をおこなう。inferは最小マッチングなので左右の文字がそれぞれ0文字に割り当てられることもあり得る。
2026-02-21
ReplaceAll
実装
type ReplaceAll<
S extends string,
From extends string,
To extends string,
> = From extends ""
? S
: S extends `${infer P}${From}${infer R}`
? `${P}${To}${ReplaceAll<R, From, To>}`
: S;
上のReplaceとほとんど同じ問題。唯一違う点としては再帰処理をおこないすべての置換対象に対して処理を行うという点のみ。Template Literal Typesの中でもう一度typeを読み出せるか、またTemplateの中なので、ジェネリックにはドルマークをつけずに呼び出せる。
2026-02-21
Capitalize
実装
type MyCapitalize<T extends string> = T extends `${infer L}${infer R}`
? `${Uppercase<L>}${R}`
: T;
Capitalizeを使う実装
type MyCapitalize<S extends string> = Capitalize<S>
replaceの問題とほとんど同じ解法で解ける。唯一必要な知識としてはUppercaseを使うことだけ。もしくはCapitalizeを使えばもっと簡単に実装できる。
2026-02-21
Chainable Options
実装
type Chainable<T = {}> = {
option<K extends string, V>(
key: K extends keyof T ? never : K,
value: V
): Chainable<Omit<T, K> & Record<K, V>>;
get(): T;
};
今までの問題よりも急に難易度が上がった印象。optionは引数を受け取り、Chainableを返す必要のある関数。getはそのままジェネリックTを返せばいい。まずTのデフォルト引数としてオブジェクトの{}を入力する。optionは、期待する入力としてキーは文字列なのでextendsで型制約をおこなう。もしTのプロパティに含まれていればneverとする。そうしてKeyの値を算出し、最終的にはOmitで新しく作りたいキーを削除したものと新しくRecordで作られたオブジェクトで&で繋ぐことで新しいオブジェクトを作り出せる。
2026-02-21
Append Argument
実装
type AppendArgument<Fn extends (...args: any) => any, A> = Fn extends (
...args: infer P
) => infer R
? (...args: [...P, A]) => R
: never;
今までの応用的な問題。関数型Fnは期待値として関数を受け取るのでextendsで制約を作る。もしFnが引数をもった関数の場合スプレッド構文を使い配列のなかで展開して新しいタプルを作ることで新しい型が追加される。
2026-02-21
Append to Object
実装
type AppendToObject<T extends {},U extends string,V> = {
[k in keyof T | U] : k extends keyof T ? T[k] : V
}
交差型を使う実装
type Merge<T> = { [K in keyof T]: T[K] }
type AppendToObject<T, U extends string, V> = Merge<T & Record<U, V>>
比較的簡単な問題。Mapped Typesで新しいオブジェクトを生成する。Tのプロパティと新しいキーのユニオンを生成し、そのキーがTであればオブジェクトTの値をT[k]で返し、そうじゃなければ新しい値Vを返す。もしくは交差型を作り、そのときにMergeという型を新しく作る。これはプロパティのキーを再列挙して新しくプロパティを作成する実装。
2026-02-21
KebabCase
実装
type KebabCase<S extends string> = S extends `${infer L}${infer R}`
? R extends Uncapitalize<R>
? `${Lowercase<L>}${KebabCase<R>}`
: `${Lowercase<L>}-${KebabCase<R>}`
: "";
知識としては今までの問題と変わりなく、一つ特殊なことといえばUncapitalizeという文字の一文字目を小文字にするTemplate Literal Typesを使えるかどうか。1文字目は無条件で小文字化するので2文字目から判定しその文字が小文字であればケバブケースにして、そうじゃなければ再び再帰処理をおこない文字を連結していく。最終的にinfer S2には空文字が入るのでそこで再帰処理は完了する。
2026-02-21
Absolute
実装
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer P}`
? `${P}`
: `${T}`;
比較的簡単な問題。気をつけることとしてTは数字しか受け取らないように型制約をおこなうこと。リテラル型を使うことで数値として入力されたものでも文字列として扱うことができる。この問題で期待されている値は文字列である。Tがもし符号付きの文字であった場合にinferで文字を推論しマイナスが取り除かれた文字を返してあげればよい。
2026-02-21
Length of String
実装
type StrToArr<S extends string> = S extends `${infer P}${infer R}`
? [unknown, ...StrToArr<R>]
: [];
type LengthOfString<S extends string> = StrToArr<S>["length"];
文字リテラルの長さを計算する問題。配列型にするとlengthで長さが得られるのは初級編でも実践済み。配列のなかでスプレッド構文を利用した再帰処理をおこなうことで最終的に配列が得られ、そこにlengthでアクセスすることで長さを取得できる。再帰処理のときunknownを使っているが、1としてもよいし値はなんでもよい。
2026-02-21
Trim Left
実装
type Space = " " | "\n" | "\t";
type TrimLeft<S extends string> = S extends `${Space}${infer P}`
? TrimLeft<P>
: S;
Trimとほとんど同じ解法でTrimの右方向のスペースを考慮しないだけで解ける。
2026-02-21
IsAlphabet
実装
type IsAlphabet<S extends string> =
Lowercase<S> extends Uppercase<S> ? false : true;
アルファベットは大文字小文字にすると文字が変化するが、そうでない文字はそのままの挙動であることをいかし、trueとfalseに分岐する。
2026-02-21
Compare Array Length
実装
type CompareArrayLength<
T extends any[],
U extends any[],
> = T["length"] extends U["length"] ? 0 : keyof T extends keyof U ? -1 : 1;
二つの配列の長さを比較し、同じ長さなら0、TがUより大きい場合は1、そうでなければ-1を返す実装。大小記号を使って単純な比較はできないのでextendsをつかって条件を網羅していく。配列はインデックスをキーに持つ特殊なオブジェクトなのでkeyofのようにプロパティを取り出そうとするとインデックスが取得される。T = [1, 2], U = [3, 4, 5]の場合、keyof T = 0 | 1, keyof U = 0 | 1 | 2になる。
2026-02-21
CheckRepeatedChars
実装
type CheckRepeatedChars<
T extends string,
U = never,
> = T extends `${infer P}${infer R}`
? P extends U
? true
: CheckRepeatedChars<R, U | P>
: false;
ジェネリック型Tのみを使う実装
type CheckRepeatedChars<T extends string> = T extends `${infer L}${infer R}` ? R extends `${any}${L}` ? true : CheckRepeatedChars<R> : false
重複文字を検出する問題。一つとして新しいUというジェネリックをつくり、それに過去に検索した文字列を持たせることで文字列の重複を見つけ出す実装をおこなった。CheckRepeatedChars<R,U | P>で再帰的に文字を呼び出すことでUには過去に検索した文字列Pが代入されていく。もう一つ、Uの状態を持たせないで実装する方法では、TをL,Rに分解してRが任意の文字列とLと一致する場合は重複したことになるのでtrue、そうじゃないときは再帰処理をおこなう。
2026-02-21
Reverse
実装
type Reverse<T extends any[]> = T extends [infer P, ...infer R]
? [...Reverse<R>, P]
: [];
作業配列Lを使う実装
type Reverse<T extends any[], L extends any[] = []> = T extends [
infer P,
...infer R,
]
? Reverse<R, [P, ...L]>
: L;
再帰処理を使い、スプレッドで前に残りの配列、後ろに最初の数値を展開していくシンプルな実装。[...Reverse<[2, 3]>, 1]、[...Reverse<[3]>, 2]、[...Reverse<[]>, 3]という流れになる。また、Lという初期値が空配列のジェネリックを作成し、再帰処理でLに値を追加していき逆配列を取得することも考えられる。
2026-02-21
Number Range
実装
type Utils<L, C extends any[] = [], R = L> = C["length"] extends L
? R
: Utils<L, [...C, 0], R | C["length"]>;
type NumberRange<L, H> = L | Exclude<Utils<H>, Utils<L>>;
考え方としては、0〜Lまでのユニオンと0〜Hまでの二つを作る。そしてその二つのユニオンから重複部分を削除することで、特定の範囲のユニオンを生成することができる。ExcludeしたときにLの値も削除されるので、L |でその値も追加しておくことが必要。
2026-02-21
Drop Char
実装
type DropChar<S, C extends string> = S extends `${infer H}${C}${infer T}`
? DropChar<`${H}${T}`, C>
: S;
Trim Leftとかなりにた問題。今回は間に入るケースもあるのでinferの二つで消したい文字を囲む。そして条件を満たすものがあればそれを削除したものを再帰処理にかけ対象の文字が削除されるまで処理を続けていく。butter flyからスペースを削除する例を考えると、${infer Head}${C}${infer Tail}がHead = ‘butter’、C = ’ ‘、Tail = ‘fly!’のように分解される。
2026-02-21
Trim Right
実装
type Space = " " | "\n" | "\t";
type TrimRight<S extends string> = S extends `${infer P}${Space}`
? `${TrimRight<P>}`
: S;
Trim Leftの反対の問題。解き方としてはSpaceを右に持ってくるか、左に持ってくるかだけの違い。
2026-02-21
Without
実装
type ToUnion<T> = T extends any[] ? T[number] : T;
type Without<T, U> = T extends [infer P, ...infer R]
? P extends ToUnion<U>
? Without<R, U>
: [P, ...Without<R, U>]
: T;
注意点としては条件Uが整数ではなく配列として複数値渡されるテストケースがあるので、配列に対してユニオン型を生成する。そして配列のなかで一文字ずつ条件Uと一致しているかを検証し、一致していたら切り取ったものを再度処理しもし合致するものがなければその値は配列として残りつつ、再帰処理をおこなっていく。
2026-02-21
Integer
実装
type Integer<T extends number> = number extends T
? never
: `${T}` extends `${infer P}.${infer R}`
? never
: T;
bigintを使う実装
type Integer<T extends number> = `${T}` extends `${bigint}` ? T : never
まず一つ目として、Tが数値であるかどうかをnumber extends Tで判定している。Tがリテラル型の場合、numberという抽象的な型に包含されないので、falseとなり数値であるという判定になる。あとはテンプレート文字を使い数値を文字列に変換して、その文字列の間に.があるかどうかで整数を判定する。もう一つの実装では、テンプレートリテラルのなかでbigint型のすべての値が文字列型として展開される性質を利用する。
2026-02-21
Zip
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/04471-medium-zip/README.md
実装
type Zip<T extends any[], U extends any[]> = T extends [infer TL, ...infer TR]
? U extends [infer UL, ...infer UR]
? [[TL, UL], ...Zip<TR, UR>]
: []
: [];
Pythonのzip関数のようなものを実装する問題。TとUというジェネリックをそれぞれ配列であるかを検証する。そしてもし両方の条件を満たせば最初の要素同士を配列として再帰処理を返していくようにする。
2026-02-21
IsTuple
実装
type IsTuple<T> = [T] extends [never]
? false
: T extends ReadonlyArray<unknown>
? number extends T["length"]
? false
: true
: false;
Tがタプル型であるかを判定する問題。[T] extends [never]はTがneverなときfalseを返すために必要な実装。T extends neverではnever extends neverのとき左側のneverはないものとして扱うためfalseの条件分岐には到達しない。number extends T['length']ここでTがタプルの場合具体的な値を持つためfalseとなりタプルということになり、Tが配列の場合はnumberとなるためfalseのほうに条件分岐する。
2026-02-24
Deep Readonly
実装
type DeepReadonly<T> = {
readonly [k in keyof T] : keyof T[k] extends never ? T[k] : DeepReadonly<T[k]>
}
再帰的にオブジェクトのプロパティにreadonlyをつけていく。ネストが深くなったとしてもreadonlyをつけていく必要があるので再帰処理を書く。プリミティブ型に対してT[k]のようにアクセスするとプロパティを持たないのでneverとなる。それをもとにConditional Typesで処理を書く。
2026-02-27
String to Union
実装
type StringToUnion<T extends string> =
T extends `${infer F}${infer R}`
? F | StringToUnion<R>
: never;
テンプレートリテラルとinferと再帰処理を組み合わせる。TypeScriptのテンプレートリテラル型においてinferを使用すると、一致する文字列の最短部分が取り出される。inferを二つ続けることで最初のinferで一文字目のみを取得でき、それ以外の処理をまた再帰処理をし最終的にUnion型でつなげることで処理が可能になる。
2026-02-27
Tuple to Union
実装
type TupleToUnion<T extends readonly any[]> = T[number]
inferを使う実装
type TupleToUnion<T> = T extends [infer U,...infer P] ? U | TupleToUnion<P> : never
非常にシンプルな実装。Tは配列を期待するようにして、その配列に対してnumberを使うとユニオン型で値を取得できる。再帰処理とinferを組み合わせることでもう一つの実装で実現できる。
2026-02-28
Trim
実装
type Space = ' ' | '\t' | '\n';
type Trim<T extends string> = T extends `${Space}${infer R}` | `${infer R}${Space}` ? Trim<R> : T
テストケースでは空白以外にも改行コードやタブなどが含まれているためそれらを除去する必要がある。ポイントはテンプレートリテラルにinfer以外の文字も普通に埋め込めることがわかるかどうか。スペースを埋め込みスペースがあれば再帰処理をおこない、そうでなければそのままの文字を返す処理をすれば最終的には得たい結果が得られる。
2026-03-20
Trunc
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/05140-medium-trunc/README.md
実装
type Trunc<T extends number | string> = `${T}` extends `${infer P}.${any}`
? `${P}` extends "-" | ""
? `${P}0`
: `${P}`
: `${T}`;
Tを小数点抜きの文字に変換するコード。基本的には${infer P}.${any}のコードで小数点を見つけ出すのが肝。ただしテストケースに合格するためには.3や-3.2など.の前に記号や数字がない場合も検出しなければならない。
2026-03-21
Shift
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/03062-medium-shift/README.md
実装
type Shift<T extends any[]> = T extends [infer P,...infer R] ? [...R] : []
考え方としては非常にシンプル。もしTが1つ以上の値をもつ配列である場合、先頭の数字がP、それ以外の数字がRとなり、その残りのスプレッドで展開した値が結果的に先頭を取り除いた値となる。
2026-03-21
All
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/18142-medium-all/README.md
実装
type All<T extends any[], U> = T extends [infer P, ...infer R]
? Equal<P, U> extends true
? All<R, U>
: false
: true;
配列の一つ一つを比較して、その値が正しければtrueを返し続ける処理をおこなう。最後配列が空になればtrueとなり、全ての配列に対して値が同じ配列であることを確認できたことを示している。Equalに関してはtype-challengesで使われている等価比較の型が用意されているので使用する。
2026-03-21
Parse URL Params
実装
type ParseUrlParams<T extends string> = `${T}` extends `${string}:${infer S}`
? S extends `${infer P}/${infer R}`
? P | ParseUrlParams<R>
: S
: never;
URLのパラメータ部分で特定の形式の文字を取得し合致するものがあればユニオンとして接続していく問題。もし合致するものがあればP | ParseUrlParams<R>で新しく発見された文字に連結していきながら再帰処理をおこなっていく。
2026-03-21
StartsWith
実装
type StartsWith<T extends string, U extends string> = T extends `${U}${infer P}`
? true
: false;
中級になっているがレベル的には初級。前方にUを含む文字があるか、残りをinferで推論する。もし含まれていればtrueを返し、そうでなければfalseを返す。
2026-03-21
EndsWith
実装
type EndsWith<T extends string, U extends string> = T extends `${infer P}${U}`
? true
: false;
先ほどのStartsWithの逆。Uがあとに来る文字列かどうかを調べればよい。
2026-03-21
Filter
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/18220-medium-filter/README.md
実装
type Filter<T extends any[], P> = T extends [infer L, ...infer R]
? L extends P
? [L, ...Filter<R, P>]
: Filter<R, P>
: [];
配列が条件に含まれているか確認。配列の一つ一つを調べていき、合致するなら結果に加えて、そうでなければ空の配列を返すようにする。
2026-03-27
OmitByType
実装
type OmitByType<T, U> = {
[K in keyof T as T[K] extends U ? never : K] : T[K]
}
解法としてはOmitとほとんど同じで、Omitはプロパティ名から削除するものを選択するのに対してこの問題は、バリューの値をもとにして削除する対象を決める。なのでasのあとにKではなく、T[K]を指定し該当する値が存在するかどうかを判定する必要がある。
2026-03-30
Readonly 2
実装
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [P in K]: T[P] & { [P in keyof T as P extends K ? never : P]: T[P] };
};
Kのプロパティを抽出してreadonlyにする、それ以外のキーはremappingしてreadonlyにならないようにする。その二つの型を&で結合して一つの型にする。型引数としてKをデフォルト値keyof Tで与えておくことで、もしKが指定されない場合でも値を取得する。
2026-03-31
Promise.all
実装
declare function PromiseAll<T extends readonly unknown[] | []>(
values: T
): Promise<{
[P in keyof T]: Awaited<T[P]>;
}>;
受け取った型TにPromiseをつけて返すようにする実装。T extends readonly unknown[] | []ここの実装は型のwideningを防ぐために渡された配列から可変しないように受け取り、なおかつ配列が空のときneverにならないために[]をつけておく。受け取った配列のキーに対してPromiseの解決型を取得するAwaitedを使い最終的な型を取得する。
2026-04-01
Remove Index Signature
実装
type RemoveIndexSignature<T,P=PropertyKey> = {
[K in keyof T as P extends K ? never : K extends P ? K : never] : T[K]
}
インデックスシグネチャで定義されているプロパティだけを除去する実装。型引数としてP=PropertyKeyを定義しておくことがポイント。P extends Kを満たす場合Kがstringなどの広い型 = インデックスシグネチャとなり、K extends Pを満たす場合Kが”name”などのリテラル型 = 通常プロパティとなりそれはそのまま残すという処理にする。
2026-04-02
Percentage Parser
実装
type PercentageParser<A extends string> =
A extends `${infer L}${infer R}`
? L extends '+' | '-'
? R extends `${infer N}%` ? [L, N, '%'] : [L, R, '']
: A extends `${infer N}%` ? ['', N, '%'] : ['', A, '']
: ['', '', '']
TypeScriptの条件型を使って、文字列を[符号、数字、%フラグ]の3要素タプルに分解する型。基本的には文字を分割して細かく条件分岐していく問題で難易度としてはそこまで高くもない。
2026-04-03
Flip Arguments
実装
type Reverse<T extends unknown[]> = T extends [infer P, ...infer U]
? [...Reverse<U>, P]
: [];
type FlipArguments<T extends (...args: any[]) => any> = T extends (
...args: infer P
) => infer U
? (...args: Reverse<P>) => U
: never;
関数の引数の型パラメータを入れ替えてしまおうという問題。引数の型を推論するようにinferをつけて得られたタプルをReverseに渡すことで逆の型を取得できるのでそれをargsのあとにつけることで要件を満たすことができる。
2026-04-04
Mutable
実装
type Mutable<T extends object> = {
-readonly [P in keyof T]: T[P];
};
readonly修飾子を取り除く問題。Mapped Typeに対して-をつけるとreadonlyを取り除くことができる。-readonlyは、既存のreadonly修飾子を取り除くという特殊な構文であり知識問題。
2026-04-05
MyUppercase
実装
type Mapping = {
a: 'A'; b: 'B'; c: 'C'; d: 'D'; e: 'E'; f: 'F'; g: 'G'; h: 'H'; i: 'I';
j: 'J'; k: 'K'; l: 'L'; m: 'M'; n: 'N'; o: 'O'; p: 'P'; q: 'Q'; r: 'R';
s: 'S'; t: 'T'; u: 'U'; v: 'V'; w: 'W'; x: 'X'; y: 'Y'; z: 'Z';
}
type MyUppercase<T extends string> = T extends `${infer F}${infer R}`
? `${F extends keyof Mapping ? Mapping[F] : F}${MyUppercase<R>}`
: ''
Capitalizeを使う実装
type MyUppercase<T extends string> = T extends `${infer F}${infer R}`
? `${Capitalize<F>}${MyUppercase<R>}`
: T;
Uppercaseを使わずに実装する問題。inferで一文字ずつ取り出し、その一文字が与えられたMappingに合致するかを調べる。ちなみにCapitalizeをつかっても実装自体は可能。
2026-04-06
Trace
実装
type Trace<T extends any[][]> = {[P in keyof T]:T[P][P & keyof T[P]]}[number]
対角線上のユニオン型を求める問題。{[P in keyof T]: ...}で入力された配列Tの各要素(行)に対してループを回す。T[P]でP番目の行を取得し、[P & keyof T[P]]でP番目の列を指定する。& keyof T[P]はPがその行の範囲内に存在することを型システムに示すためのガード。
2026-04-07
IsOdd
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/30301-medium-isodd/README.md
実装
type IsOdd<T extends number> = `${T}` extends `${any}e${any}` | `${any}.${any}`
? false
: `${T}` extends `${any}${1 | 3 | 5 | 7 | 9}`
? true
: false;
Template Literal Typesに変換し小数点や指数表記などが含まれている時はfalseにする。それ以外で末尾が奇数の場合は奇数と判断できるのでtrueにする。
2026-04-11
Subsequence
実装
type Subsequence<T extends unknown[]> = T extends [infer X, ...infer Y]
? [X, ...Subsequence<Y>] | Subsequence<Y>
: [];
配列から要素を選ぶか選ばないかを全パターン分岐させ、その組み合わせをUnion型で返す実装。例えばSubsequence<[1, 2]>の場合、パターンA(1を含める): [1, ...Subsequence<[2]>]、パターンB(1を含めない): Subsequence<[2]>というように展開され、最終的に[1, 2] | [1] | [2] | []という結果になる。
2026-04-12
Chunk
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/04499-medium-chunk/README.md
実装
type Chunk<
T extends any[],
N extends number,
Swap extends any[] = [],
> = Swap["length"] extends N
? [Swap, ...Chunk<T, N>]
: T extends [infer K, ...infer L]
? Chunk<L, N, [...Swap, K]>
: Swap extends []
? []
: [Swap];
配列を指定したサイズNごとに分割して二次元配列にする実装の問題。Swapという作業配列を用意しここに一時的に分割する配列を格納していく。分割数に達した場合はSwap配列をデフォルトの空に戻してまた作業を開始する。最終的に分割しきれなかったものは[Swap]とすることで配列に結合できるようにする。
2026-04-13
BEM style string
実装
type BEM<
B extends string,
E extends string[],
M extends string[],
> = `${B}${E extends [] ? "" : `__${E[number]}`}${M extends []
? ""
: `--${M[number]}`}`;
BEM設計ルールに基づいたCSSクラス名を型として自動生成する問題。E要素、M要素がそれぞれ配列として渡されるので候補をユニオン展開しテンプレートリテラルで渡すことで全ての組み合わせを試すことができる。テンプレートリテラル型にUnion型を渡すとTypeScriptが分配をおこない全ての組み合わせを試すという知識が必要な問題。
2026-04-15
Construct Tuple
実装
type ConstructTuple<
L extends number,
R extends unknown[] = [],
> = R["length"] extends L
? R
: ConstructTuple<L, [...R, unknown]>;
指定された長さと同じ要素数の配列を作成する問題。結果を溜めておくRという配列を初期値[]として宣言しておき、その長さがLに達するまで再帰的にコードを実行していく。
2026-04-17
Type Lookup
実装
type LookUp<U,T> = U extends {type : T} ? U : never;
Uにユニオン型を代入するとユニオン分配になり、ユニオン型のそれぞれの要素に対してConditional Typeが適用される。Uがプロパティtype、値Tを持つものであるときに型を返す。TypeScriptの型システムは構造的部分型を採用しているため、type以外のプロパティを保持していたとしても部分一致していれば互換性がある。
2026-04-17
Pop
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/00016-medium-pop/README.ja.md
実装
type Pop<T extends any[]> = T extends [...infer rest,any] ? rest: []
難易度的には中級だが、初級のFirst of Arrayとほとんど同じ解き方。最後以外の要素に対してinferを使い推論を行う。
2026-04-17
Omit
実装
type MyOmit<T, K extends keyof T> = {
[Key in keyof T as Key extends K ? never : Key] : T[Key];
}
Mapped Typesの中で、キーの値を状況によって分岐させたい場合、型アサーションであるasを使用する。neverのものはインデックスアクセスの際にも値が取得されないので、結果的に指定したキーは除去されOmitと同じ形になる。
2026-04-17
IsNever
実装
type IsNever<T> = [T] extends [never] ? true : false
この問題のキーとしてはneverを評価する際にタプルにすること。neverは要素が存在しないことを意味する型なので、分解の結果として条件型自体が評価されることなくneverが返される。そのためタプルで括ることで、T全体が1つの要素として扱われてユニオン型として分解されずにそのまま値を評価することができる。
2026-04-17
Flip
問題 https://github.com/type-challenges/type-challenges/blob/main/questions/04179-medium-flip/README.md
実装
type Flip<T extends Record<string,string | boolean | number>> = {
[k in keyof T as `${T[k]}`] : k
}
オブジェクトのキーとバリューを入れ替える問題。TはRecordでstring | boolean | numberが値のオブジェクトとして入力値を制限することで、エラーが発生するのを防ぐ。
2026-04-17
Permutation
実装
type Permutation<T, U = T> = [T] extends [never]
? []
: U extends T
? [U, ...Permutation<Exclude<T, U>>]
: [];
ユニオン型の分配法則を利用する問題。Uに'A' | 'B' | 'C'が渡されたとき、U extends TでUは全ての条件を試す。そのためUがAのとき、UがBのとき、UがCのときというふうに全ての順番を順番に実行するため、Uを先頭にしておけばそれぞれの順番で配列を生成する。またPermutation<Exclude<T,U>>のようにUを除いたユニオンを渡すことでさらに分配法則が実行され、結果的に全ての条件を網羅するようにできる。
2026-04-17
AllCombinations
実装
type StringToUnion<S extends string> = S extends `${infer F}${infer R}`
? F | StringToUnion<R>
: "";
type Combinations<T extends string, U = T> = U extends T
? U | `${U}${Combinations<Exclude<T, U>>}`
: never;
type AllCombinations<S extends string> = Combinations<StringToUnion<S>>;
ユニオン型の分配法則を利用する問題。まず文字列が渡されるので、それをユニオンに変換するStringToUnionを実装する。その後、そのユニオンに対して全ての組み合わせを列挙するCombinationsを実装する。Exclude<T, U>で除外された自分自身の要素以外と文字が連結されていくので結果的に文字が組み合わされた結果を得ることができる。Permutationはタプルで結果を返すが、Combinationsはユニオンで結果を返す。
2026-04-17
CheckRepeatedTuple
実装
type Includes<T extends any[], U> = T extends [infer F, ...infer R]
? Equal<F, U> extends true
? true
: Includes<R, U>
: false;
type CheckRepeatedTuple<T extends unknown[]> = T extends [infer F, ...infer R]
? Includes<R, F> extends true
? true
: CheckRepeatedTuple<R>
: false;
配列の一つ一つがそれ以降の配列に含まれているかを確認する。対象の値が含まれているかを調べるためにIncludesを実装し、Equal型を活用して配列のなかで含まれているものがあればtrueを返す。
2026-04-17
Diff
実装
type Diff<T,U> = Omit<T & U,keyof T & keyof U>
TとUをすべて含むプロパティからTとUが共通するプロパティ名を削除すれば、最終的に片方しか存在しないDiffを取得できる。交差型はオブジェクト型で使う場合複数のオブジェクトのプロパティが結合され、ユニオンで使うと共通のキーを値を含むものを返すのでその性質を利用する。
2026-01-21
AnyOf
実装
type Falsy = 0 | "" | false | [] | { [K: string]: never } | undefined | null;
type AnyOf<T extends readonly any[]> = T[number] extends Falsy ? false : true;
配列の各要素に条件一致させて判定していくのでnumberで配列参照する。T[number]はユニオンとして展開されT[number]がFalsyのサブタイプであるかどうか一つでもそれを満たさない値があればtrulyな値が含まれていることになる。