TypeScript Challenge
問題
組み込みの型ユーティリティPick<T, K>を使用せず、TからKのプロパティを抽出する型を実装します。(KはUnion型でもOK)
-
keyof T
型Tのkeyだけを取得する
例)
type Point = { x: number; y: number };
type P = keyof Point;
// これは x | yと同じ
-
U extends keyof T
型UはTのkeyのどれかになる -
U extends keyof T
の具体例(https://negalog.com/typescript-keyof-generics/) -
[P in K]
for(item in list)
みたいなノリ。Union型のKから型を一つずつ取り出してPに入る -
T[P]
JSのオブジェクトと同じ感じ。
type Type = {
x:number,
y:string
}
でType[x]
でnumber
がとれる。
解答
type MyPick<T , K extends keyof T> = { [P in K]: T[P] }
- 使い方
type Type = { x:number , y:string ,z:null}
MyPick<Type ,z> = {x:number,y:string}
問題
組み込みの型ユーティリティReadonly<T>を使用せず、T のすべてのプロパティを読み取り専用にする型を実装します。
type MyReadonly<T> = {readonly [K in keyof T]:T[K]};
- Tのプロパティをkeyofで取得
- inで回す
- 回した値に
readonly
をつける
問題
タプルを受け取り、その各値のkey/valueを持つオブジェクトの型に変換する型を実装します。
- タプル型
配列の型
let a : [ string ,number]
a=[true,1]//NG
a=["mmm" , 1]//OK
- T[number]でTのUnion型が取得できる(https://zenn.dev/luvmini511/articles/d89b3ad241e544 )
- T['length']でタプル型の長さを取得できる
解答
type TupleToObject<T extends readonly any[]> = {[K in T[number]]:K}
問題
組み込みの型ユーティリティExclude <T、U>を使用せず、Uに割り当て可能な型をTから除外する型を実装します。
解答
type MyExclude<T, U> = T extends U ? never : T
- T :
string|number|string[]
- U:
string[]
の場合は
type a = MyExclude<T,U>=string | number;
となる。
type MyExclude<T, U> = T extends U ? never : T
これの一番左のTと一番右のTは中身が違う。なんか不思議な感じがする。
Conditional TypesにはUnion typesの分配則がある。
(T1 | T2) extends U ? X : Y = (T1 extends U ? X : Y) | (T2 extends U ? X : Y)
なのでこれを使うと
type MyExclude<string|number|string[],string[]>
= string extends string[] ? never : string |
number extends string[] ? never : number |
string[] extends string[] ? never : string[] |
= string | number | never
= string | number
となる。頭ええな
Conditional Types!!!
参考:https://qiita.com/Quramy/items/b45711789605ef9f96de
問題
Promise ライクな型が内包する型をどのように取得すればよいでしょうか。 例えば、Promise<ExampleType>という型がある場合、どのようにして ExampleType を取得すればよいでしょうか。
解答
type MyAwaited<T extends Promise<any>> = T extends Promise<infer R>
? (R extends Promise<any> ? MyAwaited<R> : R )
: T;
PromiseにPromiseが含まれることを踏まえて再起的にMyAwaitedを使っている。
T extends U ? X : Y
みたいなやつをConditional Types とかいうらしい
参考:https://qiita.com/Quramy/items/b45711789605ef9f96de
とりあえずinferというのを知らんから調べる。
infer
infer
はconditional typeのextends
の中でしか使われない。
- 参考:https://qiita.com/ehika/items/8f41d4a3c8f9df4af9c3
-
infer
が必要な理由:https://stackoverflow.com/questions/60067100/why-is-the-infer-keyword-needed-in-typescript
なぜこれはOKで
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
なぜこれはNGなのか
type ReturnType<T> = T extends (...args: any[]) => R ? R : any;
NGである理由:Rが宣言されていないから
infer R
とすることでTから型を推論している(infer:「推論する」の意味)
改めて解答に戻ると、
T extends Promise<infer R> ? (長い何か): T
でTがPromise型じゃなければそのまま返す
(長い何か)の部分でTがPromise型だった時のことを考えている。
infer R
で取り出したPromise型の中身がさらにPromise型だったら再帰的にMyAwaited型に代入している。infer R
で取り出した型がPromiseでなければその値を返す。
すげーうまいこといってんな