search
LoginSignup
3

More than 3 years have passed since last update.

posted at

updated at

ネストされたオブジェクトのプロパティをひたすらすべて列挙して、Union Typeにするワンライナー

どこで使えるのかはわからないけれど、ネストされたオブジェクトのプロパティをひたすらすべて列挙して、Union Typeにするワンライナーができたのでメモ

type ValueOf<T> = T[keyof T];
type Property<T> = keyof T | ValueOf<{[K in keyof T]: T[K] extends object ? Property<T[K]> : never}>

ワンライナーといいつつ2行あるのはご愛嬌

以下のような感じで使える

interface User {
  name: string,
  id: string,
  obj: {
    obj2: {
      aaa: string
    },
    bbb: string,
  }
}
type ValueOf<T> = T[keyof T];
type Property<T> = keyof T | ValueOf<{[K in keyof T]: T[K] extends object ? Property<T[K]> : never}>

type UserProperty = Property<User> //=> "name" | "id" | "obj" | "obj2" | "bbb" | "aaa"

image.png

ちなみにネストされているプロパティをチェックしなくて良いなら以下で事足りる

type Property<T> = keyof T //=> "name" | "id" | "obj"

深さ指定で所得できるようにする

もはやワンライナーではないけれど、以下のように書けば深さ指定でプロパティを所得できる


// 5階層まで対応。もっと深くまで辿りたい場合は 6:5 のように増やす
type NumMap = {
    5:4,
    4:3,
    3:2,
    2:1,
    1:1,
}

type ValueOf<T> = T[keyof T];
type Property2<T,P extends keyof NumMap> = P extends 1 ?  keyof T : ValueOf<{[K in keyof T]: T[K] extends object ? Property2<T[K], NumMap[P]> : never}>

以下のように使える

interface User {
  name: string,
  id: string,
  obj: {
    obj2: {
      aaa: string
    },
    bbb: string,
  }
}

type NumMap = {
    5:4,
    4:3,
    3:2,
    2:1,
    1:1,
}

type ValueOf<T> = T[keyof T];
type Property2<T,P extends keyof NumMap> = P extends 1 ?  keyof T : ValueOf<{[K in keyof T]: T[K] extends object ? Property2<T[K], NumMap[P]> : never}>

type UserProperty = Property2<User,2> //=> "obj2" | "bbb"

image.png

まとめ

TypeScriptすごい

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3