type challengesをやる
warm-upからextremeまで一通りやりたい
easy
Pick
Implement the built-in Pick<T, K> generic without using it.
Constructs a type by picking the set of properties K from T
解答
type MyPick<T extends object, K extends keyof T> = {
[P in K]: T[P]
}
コメント
Mapped TypesとIndexed Access Typesをしょっぱなから使わせてくる
Readonly
解答
type MyReadonly<T extends object> = {
readonly [K in keyof T]: T[K]
}
コメント
この場所にreadonly
をかけることを知っているかどうかの勝負
Tuple to Object
Given an array, transform it into an object type and the key/value must be in the provided array.
解答
type TupleToObject<T extends readonly (keyof any)[]> = {
[K in T[number]]: K
}
コメント
T extends readonly (keyof any)[]
のようにプロパティのキーにできる型のみで制約をかける必要あり
T[number]
で配列Tの要素の型をUnionでとれる
First of Array
Implement a generic
First<T>
that takes an ArrayT
and returns its first element's type.
解答
type First<T extends readonly any[]> = T extends [infer F, ...any] ? F : never
コメント
infer
使えばOK
Length of Tuple
For given a tuple, you need create a generic
Length
, pick the length of the tuple
解答
type Length<T extends readonly any[]> = T['length'];
コメント
タプルなので単にlengthで長さが取れる
Exclude
Implement the built-in Exclude<T, U>
解答
type MyExclude<T, U> = T extends U ? never : T
コメント
T | never
がT
に推論されることと、Distributive Contiditional Typesを使う
Awaited
If we have a type which is wrapped type like Promise. How we can get a type which is inside the wrapped type?
解答
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer I>
? I extends PromiseLike<any>
? MyAwaited<I>
: I
: T;
コメント
PromiseLike
でthenableなオブジェクトに対応することと、ネストしているPromiseへの対応がちょっと面倒
If
Implement the util type
If<C, T, F>
which accepts conditionC
, a truthy valueT
, and a falsy valueF
.C
is expected to be eithertrue
orfalse
whileT
andF
can be any type.
解答
type If<C extends boolean, T, F> = C extends true ? T : F
コメント
特になし
Concat
Implement the JavaScript
Array.concat
function in the type system. A type takes the two arguments. The output should be a new array that includes inputs in ltr order
解答
type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U];
コメント
T
がタプルまたは配列の場合...T
で展開できる
Includes
Implement the JavaScript
Array.includes
function in the type system. A type takes the two arguments. The output should be a booleantrue
orfalse
.
解答
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer Rest]
? Equal<F, U> extends true
? true
: Includes<Rest, U>
: false;
コメント
このあたりのテストケースが通らなさ過ぎてEqual
を使ってしまった
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
一応recommended
のラベルがついている解答を見たところ同じような解答だった
急に難易度が上がったような 🤔
Push
Implement the generic version of
Array.push
解答
type Push<T extends readonly any[], U> = [...T, U];
コメント
Concat
と似たような感じでOK
Unshift
Implement the type version of
Array.unshift
解答
type Unshift<T extends readonly any[], U> = [U, ...T];
コメント
Push
と同じですね
Parameters
Implement the built-in Parameters generic without using it.
medium
Implement the built-in
ReturnType<T>
generic without using it.
解答
type MyReturnType<T extends (...a: any[]) => any> = T extends (
...any: any[]
) => infer R
? R
: never;
コメント
Parameter
の応用で行けます
Omit
Implement the built-in Omit<T, K> generic without using it.
Constructs a type by picking all properties from T and then removing K
解答
type MyOmit<T extends object, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P]
}
コメント
Excludeを使ってTのプロパティからKを取り除く
v4.1から導入されたas
を使うことでインラインで書けるらしい
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
Readonly 2
Implement a generic
MyReadonly2<T, K>
which takes two type argumentT
andK
.
K
specify the set of properties ofT
that should set to Readonly. WhenK
is not provided, it should make all properties readonly just like the normalReadonly<T>
.
解答
type MyReadonly2<T extends object, K extends keyof T = keyof T> = Readonly<
Pick<T, K>
> &
Omit<T, Extract<keyof T, K>>;
コメント
Omitの中のExtractはいらなかったかも
Deep Readonly
Implement a generic
DeepReadonly<T>
which make every parameter of an object - and its sub-objects recursively - readonly.You can assume that we are only dealing with Objects in this challenge. Arrays, Functions, Classes and so on do not need to be taken into consideration. However, you can still challenge yourself by covering as many different cases as possible.
解答
type DeepReadonly<T extends object> = {
readonly [P in keyof T]: T[P] extends Record<any, any>
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: Readonly<T[P]>;
};
コメント
Functionだけ別でチェックしているのがなんともいえない感じ
と思ったがVueの型定義も気合だったので別にいいかという気持ちに
Tuple to Union
Implement a generic
TupleToUnion<T>
which covers the values of a tuple to its values union.
解答
type TupleToUnion<T extends any[]> = T[number]
コメント
特になし
Chainable Options
Chainable options are commonly used in Javascript. But when we switch to TypeScript, can you properly type it?
In this challenge, you need to type an object or a class - whatever you like - to provide two function
option(key, value)
andget()
. Inoption
, you can extend the current config type by the given key and value. We should about to access the final result viaget
.
解答
type Chainable<C extends object = {}> = {
option<K extends keyof any, V>(
key: K extends keyof C ? never : K,
value: V
): Chainable<Omit<C, K> & { [P in K]: V }>;
get(): C;
};
コメント
K extends keyof C ? never : K
で現在定義されているプロパティを見て、定義済みであればkey: never
としてエラーにする必要がある
Last of Array
Implement a generic
Last<T>
that takes an ArrayT
and returns its last element.
解答
type Last<T extends any[]> = T extends [...any, infer R] ? R : never;
コメント
First of Array
がeasyにあるのにこっちはmediumなんだと思うなど
Pop
Implement a generic
Pop<T>
that takes an ArrayT
and returns an Array without it's last element.
解答
type Pop<T extends any[]> = T extends [...infer U, any] ? U : []
コメント
Last of Array
で...any
で拾っていた部分を返すようにする
Promise.all
Type the function PromiseAll that accepts an array of PromiseLike objects, the returning value should be
Promise<T>
whereT
is the resolved result array.
解答 ❌
declare function PromiseAll<T extends any[]>(
values: T
): Promise<{ [P in keyof T]: Awaited<T[P]> }>;
readonlyになってほしくないのになってしまったりtupleがarrayとして推論されたりで通らず
コメント
敗因:Variadic Tuple Typesを知らなかった ([...T]
)
この機能を使うことで次のように書くことができる
declare function PromiseAll<T extends any[]>(
values: readonly [...T]
): Promise<{ [P in keyof T]: Awaited<T[P]> }>;
readonly [...T]
とT extends any[]
によって、readonlyなタプルも渡せるようにしつつ、T自体はreadonlyでない、かつタプルはタプルのまま推論させるといったことができるみたいな感じ
Type Lookup
Sometimes, you may want to lookup for a type in a union to by their attributes.
In this challenge, we would like to get the corresponding type by searching for the common type field in the union
Cat | Dog
. In other words, we will expect to getDog
forLookUp<Dog | Cat, 'dog'>
andCat
forLookUp<Dog | Cat, 'cat'>
in the following example.
解答
type LookUp<U extends { type: string }, T extends U["type"]> = U extends {
type: T;
}
? U
: never;
コメント
パット見結構使いどころありそうだな~と思ったけど実例が思いつきませんでした
Trim Left
Implement
TrimLeft<T>
which takes an exact string type and returns a new string with the whitespace beginning removed.
解答
type TrimLeft<S extends string> = S extends ` ${infer R}`
? TrimLeft<R>
: S extends `\t${infer R}`
? TrimLeft<R>
: S extends `\n${infer R}`
? TrimLeft<R>
: S;
コメント
Template Literal Typesを使う
長々と書いている部分はtype Space = ' ' | '\n' | '\t'
を宣言してこれを使ったほうが間違いなくよい
Trim
Implement
Trim<T>
which takes an exact string type and returns a new string with the whitespace from both ends removed.
解答
type Space = " " | "\n" | "\t";
type Trim<S extends string> = S extends `${Space}${infer R}`
? Trim<R>
: S extends `${infer L}${Space}`
? Trim<L>
: S;
コメント
反省を生かしてSpace
を定義した
TrimLeft
した後TrimRight
すればよい
Capitalize
Implement
Capitalize<T>
which converts the first letter of a string to uppercase and leave the rest as-is.
解答
type MyCapitalize<S extends string> = S extends `${infer S}${infer R}`
? `${Uppercase<S>}${R}`
: "";
コメント
長さ1以上のstring S
に対してS extends ${infer S}${infer R}
とするとinfer S
は最初の文字にマッチするので、あとは組み込みの型であるUppercase<S>
を使って大文字にすればOK
Replace
Implement
Replace<S, From, To>
which replace the stringFrom
withTo
once in the given stringS
解答
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;
コメント
特になし
ReplaceAll
Implement
ReplaceAll<S, From, To>
which replace the all the substringFrom
withTo
in the given stringS
解答
type ReplaceAll<
S extends string,
From extends string,
To extends string
> = From extends ``
? S
: S extends `${infer L}${From}${infer R}`
? `${L}${To}${ReplaceAll<`${R}`, From, To>}`
: S;
コメント
Replace同様左からマッチさせていき、残り部分(R
)を再帰する
Append Argument
For given function type
Fn
, and any type A (any in this context means we don't restrict the type, and I don't have in mind any type 😉) create a generic type which will take Fn as the first argument, A as the second, and will produce function type G which will be the same as Fn but with appended argument A as a last one.
解答
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (
...args: infer Args
) => infer R
? (...args: [...Args, A]) => R
: never;
コメント
easyのPush
とParameter
の組み合わせ
Permutation
Implement permutation type that transforms union types into the array that includes permutations of unions.
解答 ❌
Exclude使って再帰するのかな~くらいまでしかわからず
コメント
K extends K
でUnionのそれぞれの型を評価するのと、[T] extends [never]
によるneverのチェックがポイントっぽい
type Permutation<T, K = T> = [T] extends [never]
? []
: K extends K
? [K, ...Permutation<Exclude<T, K>>]
: never;
詳しい解説は以下
Length of String
Compute the length of a string literal, which behaves like
String#length
.
解答
type StrToTuple<
S extends string,
T extends any[] = []
> = S extends `${infer F}${infer L}` ? StrToTuple<L, [...T, F]> : T;
type LengthOfString<S extends string> = StrToTuple<S>["length"];
コメント
tupleのlengthはとれるので変換したら行けた
Flatten
In this challenge, you would need to write a type that takes an array and emitted the flatten array type.
解答
type Flatten<Array> = Array extends [infer F, ...infer R]
? [...Flatten<F>, ...Flatten<R>]
: Array extends []
? []
: [Array];
コメント
lengthが1以上のタプルであれば各要素に対して再帰、空のタプルならそのまま返し、タプル以外であれば1要素のタプルとして返す
Append to object
Implement a type that adds a new field to the interface. The type takes the three arguments. The output should be an object with the new field.
解答
type AppendToObject<T extends object, U extends keyof any, V> = {
[P in keyof T | U]: P extends keyof T ? T[P] : V;
};
コメント
用意されているテストケースだったらこれでも通りそうだなと思いましたが通りませんでした...
type AppendToObject<T extends object, U extends keyof any, V> = T & {
[P in U]: V;
};
Absolute
Implement the Absolute` type. A type that take string, number or bigint. The output should be a positive number string
解答
type Absolute<T extends number | string | bigint> =
`${T}` extends `${infer F}${infer R}` ? (F extends "-" ? R : `${T}`) : `${T}`;
コメント
↓が模範解答
type Absolute<T extends number | string | bigint> =
`${T}` extends `-${infer R}` ? R : `${T}`;
String to Union
Implement the String to Union type. Type take string argument. The output should be a union of input letters
解答
type StringToTuple<T extends string> = T extends `${infer F}${infer R}` ? [F, ...StringToTuple<R>] : []
type StringToUnion<T extends string> = StringToTuple<T>[number];
コメント
↓模範解答
type StringToUnion<T extends string> = T extends `${infer Letter}${infer Rest}`
? Letter | StrintToUnion<Rest>
: never;
Merge
Merge two types into a new type. Keys of the second type overrides keys of the first type.
解答
type Merge<F, S> = {
[P in Exclude<keyof F, keyof S> | keyof S]: P extends keyof S
? S[P]
: P extends keyof F
? F[P]
: never;
};
コメント
Sを優先して参照する
type Merge<F, S> = Omit<F, keyof S> & S;
だとなぜかテストが通らず(推論される型としてはあってそうなんですが)
KebabCase
Replace the
camelCase
orPascalCase
string withkebab-case
.
FooBarBaz
->foo-bar-baz
解答
type K = "-" | "_";
type _Kebab<S> = S extends `${infer L}${infer R}`
? L extends Lowercase<L> | K
? `${L}${_Kebab<R>}`
: `-${Lowercase<L>}${_Kebab<R>}`
: S;
type KebabCase<S> = _Kebab<S> extends `-${infer R}`
? R extends ""
? S
: R
: _Kebab<S>;
コメント
Uncapitalizeが完全に頭から抜けていた
Object
Get an
Object
that is the difference betweenO
&O1
解答
type Diff<O, O1> = {
[P in
| Exclude<keyof O, keyof O1>
| Exclude<keyof O1, keyof O>]: P extends keyof O
? O[P]
: P extends keyof O1
? O1[P]
: never;
};
コメント
↓模範解答っぽいもの なるほどな~
type Diff<O, O1> = Omit<O & O1, keyof (O | O1)>;
AnyOf
Implement Python liked
any
function in the type system. A type takes the Array and returns true if any element of the Array istrue
. If the Array is empty, returnfalse
.
解答
type IsFalsy<T> = T extends [] | undefined | null | "" | 0 | false
? true
: T extends object
? [keyof T] extends [never]
? true
: false
: false;
type AnyOf<T extends readonly any[]> = [
{
[K in keyof T]: IsFalsy<T[K]> extends true ? never : true;
}[number]
] extends [never]
? false
: true;
コメント
TのUnionがFalsyな型のUnionのサブセットかどうかを見ればよいらしい めっちゃスマート
type AnyOf<T extends any[]> = T[number] extends
| 0
| ""
| false
| []
| { [key: string]: never }
| undefined
| null
? false
: true;
IsNever
Implement a type IsNever, which takes input type T. If the type of resolves to never, return true, otherwise false.