Closed35

Type challenge / Warmup & Easy やっていく

snakasnaka

Type challenge / Warmup & Easy やっていく
#snaka_type_challenge

https://type-challenges-judge.pages.dev/

snakasnaka

しょっぱなの Pick から躓いている
一度わかっても考え方を忘れるのでメモる

Pick

type MyPick<T, K extends keyof T> = { [Key in K]: T[Key] }

まず、 MyPick<T, K extends keyof T>K に渡すリテラルを T の key に含まれるものに限定する

これで以下のテストの条件をクリアする

  // @ts-expect-error
  MyPick<Todo, 'title' | 'completed' | 'invalid'>,

つぎに { [Key in K]: Key extends K ? T[Key] : never } この部分について

  • [Key in K]K に渡された Union の型一つづつ取り出し Key にセット
  • T[Key]T 型のプロパティ Key の型を取り出す
snakasnaka

ググって答えにたどり着いたが [Key in K]: T[Key][Key in K] の部分がぱっと出てこなかった

snakasnaka

なるほど as
Property に対する getter の型を定義するみたいなこともできるのか

type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
snakasnaka

Readonly

Mappded Type 理解したら Readonly は簡単だった

type MyReadonly<T> = { readonly [Key in keyof T]: T[Key] }
snakasnaka

Tuple to Object

なんとかできた

type TupleToObject<T extends readonly string[]> = { [Key in T[number]]: Key }
snakasnaka

First of Array

空の配列を除外するのに Conditional type で T extends [] の判定を使う発想が出てこなかった

type First<T extends any[]> = T extends [] ? never : T[0]
snakasnaka

Length of Tuple

type Length<T> = 
  T extends readonly [any, any, any, any] ? 4
  : T extends readonly [any, any, any, any, any] ? 5
  : never

愚直に書いてみるが tuple 意外をエラーにするのができていない

snakasnaka

愚直にやったらテストは通った

type Length<T extends readonly any[]> = 
  T extends readonly [any, any, any, any] ? 4
  : T extends readonly [any, any, any, any, any] ? 5
  : never
snakasnaka

型からメソッド呼べるのか...?

ちがうか
長さが固定である tuple は length プロパティの型として、その tuple の長さを表す数値が割り当てられているのか

snakasnaka
const tuple = ['a', 'b', 'c'] as const
const len = tuple.length // => len: 3 という型

tuple が可変になる ( array になる ) と結果が変わる

const tuple = ['a', 'b', 'c']
const len = tuple.length // => len: number という型
snakasnaka

Exclude

雰囲気で解けた

type MyExclude<T, U> = T extends U ? never : T

TypeScript: Documentation - Conditional Types

Conditional Type において T に Union Type を渡したときには、それぞれに条件を適用した結果を Union として返す ( never は Union から取り除かれる )

snakasnaka

Awaited

type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer P> ? P : never 

これだと以下が満たせない

  Expect<Equal<MyAwaited<Z>, string | number>>,

Promise の入れ子か

type Z = Promise<Promise<string | number>>
snakasnaka

愚直にこれで通ったけど、良いやりかたありそう

type MyAwaited<T extends Promise<unknown>> =
  T extends Promise<infer P0>
    ? P0 extends Promise<infer P1> 
      ? P1
      : P0
    : never 
snakasnaka

再帰で書き直した

type MyAwaited<T extends Promise<any>> =
  T extends Promise<infer P>
    ? P extends Promise<any> 
      ? MyAwaited<P>
      : P
    : never 
snakasnaka

これでも行けそうなんだけど...

type MyAwaited<T> =
  T extends Promise<infer P>
    ? MyAwaited<P>
    : T

エラーとなるべきパターンがエラーにならない

// @ts-expect-error
type error = MyAwaited<number>
snakasnaka

だからといって型パラメタの制約として extends Promise<any> を与えると...

type MyAwaited<T extends Promise<any>> =
  T extends Promise<infer P> // <= P が制約に合わないのでエラー
    ? MyAwaited<P>
    : T

となるので、最終的に前述のようなコードになっている

type MyAwaited<T extends Promise<any>> =
  T extends Promise<infer P>
    ? P extends Promise<any> 
      ? MyAwaited<P>
      : P
    : never 

きっと、良い方法ありそう


みんなの回答例を見るとこれがベストみたい

snakasnaka

If

これは簡単

type If<C extends boolean, T, F> = C extends true ? T : F
snakasnaka

Concat

なんとなくで書いたら合ってたっぽい

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

もうちょっとシンプルにできた

type Concat<T extends any[], U extends any[]> = [...T,...U]
snakasnaka

Includes

type Includes<T extends readonly any[], U> =
  T extends [infer V, ...(infer W)]
    ? V extends U 
      ? true
      : Includes<W, U>
    : T extends [infer V]
      ? V extends U
        ? true
        : false
      : false 

ちょっと違うらしい。
より厳密なチェックが必要そう
以下のテストが失敗している

  Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
  Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
snakasnaka

うーん、Equal<T,U> 使ってる回答あるけど...
それはちょっと違うよな

snakasnaka

謎のUtility Type _Equal の型定義むずい

type _Equal<T, U> =
  (<K>() => K extends T ? 1 : 2) extends (<K>() => K extends U ? 1 : 2) 
    ? true
    : false 

type Includes<T extends readonly any[], U> =
  T extends [infer F, ...infer R]
    ? _Equal<U, F> extends true
      ? true
      : Includes<R, U>
  : false

これで通るけど _Equal の K がどこから出てきてるのかよくわからんな

参考: 898 - Includes · Issue #29174 · type-challenges/type-challenges

snakasnaka

Push

これは簡単

type Push<T extends any[], U> = [...T,U]
snakasnaka

Unshift

Push の逆にしただけ

type Unshift<T extends any[], U> = [U,...T]
snakasnaka

Parameters

Awaited とかと比べると全然かんたん

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

T extends の無駄な繰り返しは削れた

type MyParameters<T> =
  T extends (...args: infer U) => any
    ? U
    : never
このスクラップは5ヶ月前にクローズされました