Open25

type challenge

み

Length of Tuple

easy

length.ts
type Length<T extends readonly any[]> = T['length'];

インデックスアクセサ['length']がいまいち腹落ちしてない

み

Exclude

easy

exclude.ts
type MyExclude<T, U> = T extends U ? never : T;

TとしてUnionが渡るとdistributionによってそれぞれの型に対して処理が行われたUnionが返る。

み

Await

easy

await.ts
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer U> ? U extends Promise<unknown> ? MyAwaited<U> : U : never;

inferによって推論した型を利用できる(Conditionalでのみ利用可能)
型定義から再帰ができる

み

If

easy

if.ts
type If<C extends boolean, T , F> = C extends true ? T : F;

extends true==的な表現をする

み

Includes

easy

includes.ts
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・Equalextends true・再帰を組み合わせて型定義のなかでループを表現
個人的に「ほぉ〜」ってなった

み

Concat

easy

concat.ts
type Concat<T extends any[], U extends any[]> = [...T, ...U];

spread operatorを利用
簡潔にかけておおってなった

み

Push, Unshift

easy

push-unshift.ts
type Push<T extends any[], U> = [...T, U];
type Unshift<T extends any[], U> = [U, ...T];

spread operator

み

Parameters

easy

parameters.ts
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;

argsをとるのにこんな方法があるのか、と思った

み

Return Type

medium

return-type.ts
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer U ? U : never;

parametersの戻り値のほうをinferしたバージョン

み

Omit

medium

omit.ts
type MyOmit<T, K extends keyof T> = {[k in Exclude<keyof T, K>]: T[k]};

Excludeをつかってkeyof TKの差集合をとる

み

Readonly2

medium

readonly2.ts
type MyReadonly2<T, K extends keyof T = keyof T> = {readonly [k in K] : T[k]} & Omit<T, K>;

keyof TKの差集合の扱いに困った。&をつかって結合する
Kの初期値をkeyof Tで与えてあげる

み

Deep Readonly

medium

deep-readonly.ts
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

tuple-to-union.ts
type TupleToUnion<T extends any[]> = T[number];

配列のインデックスアクセサでUnionを得る

み

Chainable

medium

chainable.ts
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

last.ts
type Last<T extends any[]> = T extends [...any, infer U] ? U: never;

スプレッド構文とinferで元の配列を分割する

み

Pop

medium

pop.ts
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

lookup.ts
type LookUp<U , T> = U extends {type: T} ? U : never;

U extends {type: T}でTの存在確認

み

TrimLeft

medium

trim-left.ts
type WhiteSpaces = ' ' | '\n' | '\t';
type TrimLeft<S extends string> = S extends `${WhiteSpaces}${infer R}` ? TrimLeft<R> : S;

再帰をつかって左から一文字ずつ処理、ホワイトスペースとinferの組み合わせがミソ

み

Trim

medium

trim.ts
type WhiteSpaces = ' ' | '\n' | '\t';
type Trim<S extends string> = S extends `${WhiteSpaces}${infer R}` | `${infer R}${WhiteSpaces}` ? Trim<R> : S;

TrimLeftの発展版、左右のホワイトスペースへの対応をUnionで表現

み

Capitalize

medium

capitalize.ts
type MyCapitalize<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}`: S;

Trimと同様

み

Replace

medium

replace.ts
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

replace-all.ts
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

append-argument.ts
type AppendArgument<Fn, A> = Fn extends (...args: infer U) => infer R ? (...args: [...U, A]) => R : never;