Closed15

💻 Type challenge / Medium (1 of 10) やっていくスレ

snakasnaka

Get Return Type

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

可変引数の書き方がいつもわからなくなるが、なんとかクリアできた

snakasnaka

Omit

なんとか自力で解けた

type MyOmit<T, K extends keyof T> = {
  [Key in keyof T as Key extends K ? never : Key]: T[Key]
}

いくつかポイントがあった

  • K extends keyof TK に渡す型を T のプロパティ名に一致するリテラルに限定する
  • Key in keyof TT のプロパティ名(key)をひとつづつ取り出して Key に割り当てる
  • [... as Key extends K ? never : Key]KeyK で示されたリテラルと一致するときは never とすることで、そのプロパティを結果から除外する。そうでないときは Key そのまま
  • 最後 T[Key]T の型から Key の名前を持つプロパティの型を取り出す
snakasnaka

Readonly2

すこしづつ TypeScript の型システムの気持ちがわかるようになってきたかも

type MyReadonly2<T, K extends keyof T = any> =
  { readonly [U in keyof T as U extends K ? U : never]: T[U] } &  // ① K に該当するプロパティだけを抽出 し readonly にする
  { [U in keyof T as U extends K ? never : U]: T[U] }  // ② K に該当しないプロパティだけを抽出

考え方のポイント:

  • Omit でやったことの応用で、 T のプロパティから K に該当するものだけを抽出した型 (①) と、それ意外を抽出した型 (②) を取り出す
  • ① に readonly を付ける
  • ② には何もつけずそのまま
  • ① と ② をマージした型を最終的に作る
  • K が省略した場合のデフォルトは any とすることで、全てのプロパティが readonly 対象となる
snakasnaka

DeepReadonly

object から関数を除く部分がちょっと納得行っていない。
もっと良いやりかたありそう

type DeepReadonly<T> = {
  readonly [U in keyof T]: T[U] extends object
                            ? T[U] extends (...args: any[]) => any
                              ? T[U]
                              : DeepReadonly<T[U]>
                            : T[U]
}
snakasnaka

別解を見てみる

type DeepReadonly<T> = keyof T extends never
  ? T
  : { readonly [k in keyof T]: DeepReadonly<T[k]> };

keyof T extends neverT が key を持つ存在 ( = object ) かどうかを判定している。
なるほど。

snakasnaka

これがよくわからない

type DeepReadonly<T> =
  { readonly [P in keyof T]: T[P] extends { [i: number]: unknown }
    ? DeepReadonly<T[P]>
    : T[P] }

{ [i: number]: unknown } これで 関数を除いたオブジェクトにマッチする理屈がよくわかっていない

type IsObject<T> = T extends { [i: number]: unknown } ? true : false

type obj0 = IsObject<{ 1: 'a' }>  // true
type obj1 = IsObject<[1, 2, 3]> // true
type obj2 = IsObject<[1, 'a', null]> // true
type obj3 = IsObject<{ a: 'A' }> // true
type obj4 = IsObject<() => {}> // false
snakasnaka

TupleToUnion

これはすぐ解けた

type TupleToUnion<T extends [...any]> = T[number]
snakasnaka

別解も見てみる

type TupleToUnion<T extends any[]> = T[number]

だいたい自分の解答もベストに近いように思われる

snakasnaka

Chainable Options

再帰つかえばできそうなイメージがあるが、なかなかうまくいかない。
もうちょっと自力で頑張りたいが一旦パス

snakasnaka

Last of Array

これは簡単

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

ほかの人の回答みるといろんな解法があるとわかって面白い

snakasnaka

Chanable Options (retry)

自力は諦めて他の人の回答を参考にした

type Chainable<T = {}> = {
  option<Key extends string,Value>(
    key: Key extends keyof T ? never : Key,
    value: Value
  ): Chainable<T & { [key in Key]: Value }>
  get(): T
}

型引数を受け取れる場所が Chanable< ... > の箇所だけだという思い込みがあった

それもあって、Key extends keyof T ? never : Key で、既存の key の指定を禁止するという発想ができなかった。

このスクラップは2023/12/09にクローズされました