type challenge
途中からだけどType Challengeの記録として書いておく。
Length of Tuple
easy
type Length<T extends readonly any[]> = T['length'];
インデックスアクセサ['length']がいまいち腹落ちしてない
Exclude
easy
type MyExclude<T, U> = T extends U ? never : T;
TとしてUnionが渡るとdistributionによってそれぞれの型に対して処理が行われたUnionが返る。
Await
easy
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer U> ? U extends Promise<unknown> ? MyAwaited<U> : U : never;
infer
によって推論した型を利用できる(Conditionalでのみ利用可能)
型定義から再帰ができる
If
easy
type If<C extends boolean, T , F> = C extends true ? T : F;
extends true
で==
的な表現をする
Includes
easy
type Includes<T extends readonly any[], U> = T extends [infer X, ...infer Y] ? Equal<X, U> extends true ? true : Includes<Y, U> : false;
infer
・spread operator・Equal
・extends true
・再帰を組み合わせて型定義のなかでループを表現
個人的に「ほぉ〜」ってなった
Concat
easy
type Concat<T extends any[], U extends any[]> = [...T, ...U];
spread operatorを利用
簡潔にかけておおってなった
Push, Unshift
easy
type Push<T extends any[], U> = [...T, U];
type Unshift<T extends any[], U> = [U, ...T];
spread operator
Parameters
easy
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;
argsをとるのにこんな方法があるのか、と思った
Return Type
medium
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer U ? U : never;
parametersの戻り値のほうをinferしたバージョン
Omit
medium
type MyOmit<T, K extends keyof T> = {[k in Exclude<keyof T, K>]: T[k]};
Exclude
をつかってkeyof T
とK
の差集合をとる
Readonly2
medium
type MyReadonly2<T, K extends keyof T = keyof T> = {readonly [k in K] : T[k]} & Omit<T, K>;
keyof T
とK
の差集合の扱いに困った。&
をつかって結合する
Kの初期値をkeyof T
で与えてあげる
Deep Readonly
medium
type DeepReadonly<T> = {readonly [k in keyof T]: keyof T[k] extends never ? T[k]: DeepReadonly<T[k]>};
valueがObjectか判定して再帰呼び出しする
Objectかどうか?をextends never
で「Objectでないこと」の否定として判定
Tuple to Union
medium
type TupleToUnion<T extends any[]> = T[number];
配列のインデックスアクセサでUnionを得る
Chainable
medium
type Chainable<T extends Record<string, unknown> ={}> = {
option<K extends string, V = unknown>(key: K, value: V): Chainable<T & Record<K, V>>
get(): T
}
Recordでオブジェクト型を定義する、&結合
Last
medium
type Last<T extends any[]> = T extends [...any, infer U] ? U: never;
スプレッド構文とinferで元の配列を分割する
Pop
medium
type Pop<T extends any[]> = T extends [...infer R, infer _] ? R : never;
Lastと同様。末尾の要素は使わないので定数名を当てずにアンダースコア(Dartといっしょ)
PromiseAll
medium
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<{[k in keyof T]: T[k] extends Promise<infer U> ? U : T[k]}>;
ちゃんと理解できてない
Lookup
medium
type LookUp<U , T> = U extends {type: T} ? U : never;
U extends {type: T}
でTの存在確認
TrimLeft
medium
type WhiteSpaces = ' ' | '\n' | '\t';
type TrimLeft<S extends string> = S extends `${WhiteSpaces}${infer R}` ? TrimLeft<R> : S;
再帰をつかって左から一文字ずつ処理、ホワイトスペースとinferの組み合わせがミソ
Trim
medium
type WhiteSpaces = ' ' | '\n' | '\t';
type Trim<S extends string> = S extends `${WhiteSpaces}${infer R}` | `${infer R}${WhiteSpaces}` ? Trim<R> : S;
TrimLeftの発展版、左右のホワイトスペースへの対応をUnionで表現
Capitalize
medium
type MyCapitalize<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}`: S;
Trimと同様
Replace
medium
type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer F}${From}${infer R}` ? `${F}${To}${R}` : S;
Replace All
medium
type ReplaceAll<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer F}${From}${infer R}` ? `${F}${To}${ReplaceAll<R, From, To>}` : S;
Append Argument
medium
type AppendArgument<Fn, A> = Fn extends (...args: infer U) => infer R ? (...args: [...U, A]) => R : never;