2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ユニークビジョン株式会社Advent Calendar 2023

Day 21

ネストしたオブジェクトの指定したパス以下の値だけを受け付ける型を作成してみた

Last updated at Posted at 2023-12-20

完成したもの

// このようなデータがあるとき、
const ProgrammingLanguages = {
  FRONTEND: 'TypeScript',
  BACKEND: {
    SERVER_SIDE: 'Rust',
    DATABASE: {
      SQL: 'PostgreSQL',
      NoSQL: 'DynamoDB'
    }
  },
  MOBILE: {
    ANDROID: 'Kotlin',
    IOS: 'Swift'
  }
} as const;

// 型パラメータにパスを渡すことで、それ以下の値だけを受け付ける文字列リテラル型を作成できる。
type LangFrontend = Language<'FRONTEND'>; // 'TypeScript'
type LangMobile = Language<'MOBILE'>; // 'Kotlin' | 'Swift'
type LangDatabase = Language<'BACKEND.DATABASE'>; // 'PostgreSQL' | 'DynamoDB'
type LangSql = Language<'BACKEND.DATABASE.SQL'>; // 'PostgreSQL'

コード

type Recursive<T, K> = K extends `${infer A}.${infer B}`
  ? A extends keyof T
    ? Recursive<T[A], B>
    : never
  : K extends keyof T
    ? T[K] extends string
      ? T[K]
      : Recursive<T[K], keyof T[K]>
    : never
  ;

type Language<K> = Recursive<typeof ProgrammingLanguages, K>;

解説

K extends `${infer A}.${infer B}`

渡されたキーが . を含む場合、 Recursive<T[A], B> で一つ深いネストに対し再帰的に同じ処理を行う。

'BACKEND.DATABASE.SQL' のように複数含む場合は最初の . の前後で区切られる('BACKEND.DATABASE.SQL''BACKEND' & 'DATABASE.SQL')。

  : K extends keyof T
    ? T[K] extends string
      ? T[K]
      : Recursive<T[K], keyof T[K]>
    : never

渡されたキーが . を含まない場合、値が string 型になるまで再帰して探索し、それらのユニオン型を作成する。

おまけ

型パラメータに渡すパスの候補を出してくれるようにする。

以下のコードは、キーを再帰的に探索し . で繋げたすべてのパターンのユニオン型を作成している。

type Join<A extends string, B extends string> = A extends '' ? B : `${A}.${B}`;

type Path<T, P extends string = ''> = keyof T extends infer K extends keyof T
  ? K extends string
    ? Join<P, K> | (T[K] extends string ? never : Path<T[K], Join<P, K>>)
    : never
  : never;

type Language<K extends Path<typeof ProgrammingLanguages>> = Recursive<typeof ProgrammingLanguages, K>;

image.png

まとめ

TypeScript の型システムは慣れれば面白いものがたくさん作れそうです。

よき TypeScript ライフを!

2
0
0

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
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?