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])
}
}
}
c
配下は level2
以降は undefined
となります。
a
配下でも level2
に undefined
を指定した場合には level3
の候補はなしです。
JSDoc の内容
型定義
multiDimensional
を対象としてキーを候補としたいので typeof
を利用して型定義を実施しています。
MultiDimensional
type MultiDimensional = typeof multiDimensional
ジェネリクスの指定
引数の候補を切り替えるために各引数にはジェネリクスの指定をしています。
TypeScript でのつじつまを合わせるため記述が増えています。
-
level1
に指定するT1
にはMultiDimensional
のキーを指定 -
level2
はlevel1
により切り替わるため、MultiDimensional[T1]
がオブジェクトの場合にそのキーを指定 -
level3
はlevel2
と同様
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