1
2

More than 3 years have passed since last update.

Conditional Types にて関数の第一引数により第二引数以降の候補を切り替える

Last updated at Posted at 2020-12-06

Conditional Types にて関数の第一引数により第二引数以降の候補を切り替える

以下のようなオブジェクトがあった場合に、
第一引数には a, b, c を、
第二引数には第一引数により取得されるオブジェクトの配下のキー (a1, a2, b1, ・・・) を、
第三引数には第二引数により取得されるオブジェクトの配下のキーを指定したいとします。

multi-dimensional-object
const multiDimensional = {
  a: {
    a1: {
      AAA: 1,
      BBB: 2,
      CCC: 3,
    },
    a2: {
      DDD: 4,
      EEE: 5,
    },
  },
  b: {
    b1: 6,
    b2: 7,
  },
  c: 8,
}

実装

実装は以下のようになります。
JSDoc にて TypeScript を利用してますが JavaScript になります。

multi-dimensional-access
const multiDimensional = {
  a: {
    a1: {
      AAA: 1,
      BBB: 2,
      CCC: 3,
    },
    a2: {
      DDD: 4,
      EEE: 5,
    },
  },
  b: {
    b1: 6,
    b2: 7,
  },
  c: 8,
}

/**
 * @typedef {typeof multiDimensional} MultiDimensional
 */
/**
 * 多次元オブジェクトで引数を切替
 * @template {keyof MultiDimensional} T1
 * @template {MultiDimensional[T1] extends object ? keyof MultiDimensional[T1] : undefined} T2
 * @template {T2 extends keyof MultiDimensional[T1] ? MultiDimensional[T1][T2] extends object ? keyof MultiDimensional[T1][T2] : undefined : undefined} T3
 * @param {T1} level1
 * @param {T2} [level2]
 * @param {T3} [level3]
 */
const multiDimensionalAccess = (level1, level2, level3) => {
  console.log(multiDimensional[level1])
  if (level2) {
    console.log(multiDimensional[level1][level2])
    if (level3) {
      console.log(multiDimensional[level1][level2][level3])
    }
  }
}

a 配下の場合 level3 まで候補が存在します。
image.png

b 配下の場合候補があるのは level2 までです。
image.png

c 配下は level2 以降は undefined となります。
image.png

a 配下でも level2undefined を指定した場合には level3 の候補はなしです。
image.png

JSDoc の内容

型定義

multiDimensional を対象としてキーを候補としたいので typeof を利用して型定義を実施しています。

MultiDimensional
type MultiDimensional = typeof multiDimensional

ジェネリクスの指定

引数の候補を切り替えるために各引数にはジェネリクスの指定をしています。
TypeScript でのつじつまを合わせるため記述が増えています。

  • level1 に指定する T1 には MultiDimensional のキーを指定
  • level2level1 により切り替わるため、 MultiDimensional[T1] がオブジェクトの場合にそのキーを指定
  • level3level2 と同様
MultiDimensionalAccess
type MultiDimensionalAccess<
  // `level1` に指定する `T1` には `MultiDimensional` のキーを指定
  T1 = keyof MultiDimensional,
  T2 = T1 extends keyof MultiDimensional
    // `level2` は `level1` により切り替わるため、 `MultiDimensional[T1]` がオブジェクトの場合にそのキーを指定
    ? MultiDimensional[T1] extends object
      ? keyof MultiDimensional[T1]
      : undefined
    : undefined,
  T3 = T1 extends keyof MultiDimensional
    ? T2 extends keyof MultiDimensional[T1]
      // `level3` は `level2` と同様
      ? MultiDimensional[T1][T2] extends object
        ? keyof MultiDimensional[T1][T2]
        : undefined
      : undefined
    : undefined
> = (level1: T1, level2?: T2, level3?: T3) => void
1
2
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
1
2