💻 Type challenge / Medium (2 of 10) やっていくスレ
引っ越し元:
Pop
これも Last とかやってたら簡単
type Pop<T extends any[]> = T extends [...(infer Left), infer _] ? Left : never
別解みたところ infer _
の箇所は any
とか unknown
にしても良さそう
Promise.all
むずいな...
const promiseAllTest1 = PromiseAll([1, 2, 3] as const)
// => PromiseAll([1, 2, 3] as const): Promise<[1, 2, 3]>
以下は愚直に書いた例だけど、この結果を柔軟に取り出すにはどうするんだっけ?
declare function PromiseAll<T extends readonly unknown[]>(values: T): Promise<[T[0], T[1], T[2]]>
T
の型制約を Tuple として書くとよさそう
declare function PromiseAll<T extends readonly [...any]>(values: T): Promise<T>
以下で readonly
はずせた
これで1つのテストケースはクリアできたが、あと2つ
declare function PromiseAll<T extends readonly [...any]>(values: T):
T extends readonly [...any: infer U]
? Promise<U>
: Promise<T>
他の人の回答見てる
Dowanna さん回答 - type-challenges-judge
// T extends readonly unknown[]だとreadonly配列が返却されるため[...T]が必要
declare function PromiseAll<T extends unknown[]>(args: readonly [...T]): Promise<{
[P in keyof T]: T[P] extends Promise<infer S> ? S : T[P]
}>
配列も X in keyof Y
使えるのか、 Object っぽい型だけだと思いこんでいた
関数に readonly の値を渡すためには引数の定義の箇所でも明示的に readonly
とする必要があったのか
そのあたりの理解があいまいだった
type Resolved<T extends readonly [...any]> = {
[K in keyof T]: T[K] extends Promise<infer U> ? U : T[K]
}
declare function PromiseAll<T extends [...any]>(values: readonly [...T]): Promise<Resolved<T>>
Playground のテストは通ったけど Judge が通らなかった
通っているやつと見比べる
( ACCEPT になっているやつ)
declare function PromiseAll<T extends unknown[]>(args: readonly [...T]): Promise<{
[P in keyof T]: T[P] extends Promise<infer S> ? S : T[P]
}>
ほとんど変わらないと思うけど、以下でもダメだな...
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{
[K in keyof T]: T[K] extends Promise<infer U> ? U : T[K]
}>
ACCEPT になっているはずの回答をそのまま提出しても WRONG ANSWER になってしまうのでジャッジが間違っている可能性がある
Type Lookup
むずかしく考えて解けなかった。
type LookUp<U, T> = U extends { type: T } ? U : never
Discriminated Union の考え方を型に適用できるはず、という発想はあったが U['type'] extends T
のように判定を行っていたのがダメだった
(ダメだった例)
type LookUp<U extends { type: string }, T> = U['type'] extends T ? U : never
Trim Left
template literal で infer が使えたというのを思い出せた時点で勝てた
type TrimLeft<S extends string> =
S extends ` ${infer T}` | `\n${infer T}` | `\t${infer T}`
? TrimLeft<T>
: S
他の人の例を見て、もうちょっとシンプルにできた
type TrimLeft<S extends string> =
S extends `${ ' ' | '\n' | '\t' }${infer T}`
? TrimLeft<T>
: S
Trim
素直にやるとこうなるが、これでよいのかな?
type Space = ` ` | '\n' | `\t`
type Trim<S extends string> =
S extends `${Space}${infer T}` | `${infer T}${Space}`
? Trim<T>
: S
Capitalize
Capitalize
そのまま使う... のか?
type MyCapitalize<S extends string> = Capitalize<S>
なんか違う気がする....
infer で文字列を First / Rest に分割できることを知ったので
愚直にやってみた
type MyUpper<S extends string> =
S extends 'a' ? 'A'
: S extends 'b' ? 'B'
: S extends 'c' ? 'C'
: S extends 'd' ? 'D'
: S extends 'e' ? 'E'
: S extends 'f' ? 'F'
: S extends 'g' ? 'G'
: S extends 'h' ? 'H'
: S extends 'i' ? 'I'
: S extends 'j' ? 'J'
: S extends 'k' ? 'K'
: S extends 'l' ? 'L'
: S extends 'm' ? 'M'
: S extends 'n' ? 'N'
: S extends 'o' ? 'O'
: S extends 'p' ? 'P'
: S extends 'q' ? 'Q'
: S extends 'r' ? 'R'
: S extends 's' ? 'S'
: S extends 't' ? 'T'
: S extends 'u' ? 'U'
: S extends 'v' ? 'V'
: S extends 'w' ? 'W'
: S extends 'x' ? 'X'
: S extends 'y' ? 'Y'
: S extends 'z' ? 'Z'
: S
type MyCapitalize<S extends string> =
S extends `${infer First}${infer Rest}`
? `${MyUpper<First>}${Rest}`
: ''
なるほど Mapping が使えるか...
mapping バージョン
type LtoU = {
'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 MyCapitalize<S extends string> =
S extends `${infer F}${infer R}`
? F extends keyof LtoU
? `${LtoU[F]}${R}`
: `${F}${R}`
: ''
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
From が ''
の場合は S をそのまま
それ意外は From にマッチした部分を To に置き換え
From の前後に infer L / R 入れて To を組み立てるのがポイント
ここまでの進捗
Promise.all がなぜか Accept にならないのが残ってる