The initial approach to solving this issue is outlined in Tao's response, but the challenge arises when additional properties are added and it does not function as anticipated:
type Foo = {
outer: {
inner: any;
}
outer2: {
inner2: any;
}
}
type PropertyList<T, K1 extends keyof T = keyof T, K2 extends keyof T[K1] = keyof T[K1]> = [K1, K2];
let myList:PropertyList<Foo> = ["outer", "inner"] // This results in an error because K2 must be a property of both outer and outer2
To address this issue, we can utilize a function to assist with inferring the correct type based on the provided parameters. A two-function approach is necessary here as generic parameters for T
, K1
, and
K2</code are required, but only <code>T
should be specified:
function pathFor<T>() {
return function <K1 extends keyof T, K2 extends keyof T[K1]>(outer: K1, inner: K2): [K1,K2]{
return [outer, inner];
}
}
// Example of usage
let myList = pathFor<Foo>()("outer", "inner"); // Typed as ["outer, "inner"]
let myList2 = pathFor<Foo>()("outer2", "inner"); // Error - inner is not part of outer2
let myList3 = pathFor<Foo>()("outer2", "inner2"); // Typed as ["outer2, "inner2"]
Edit
Another option is to expand the function to accommodate paths up to a finite length (4 in this example, but more can be added as needed):
function keysFor<T>() {
function keys<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]>(outer: K1, inner: K2, innerInner: K3, innerInnerInner: K4): [K1,K2, K3, K4]
function keys<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(outer: K1, inner: K2, innerInner: K3): [K1,K2, K3]
function keys<K1 extends keyof T, K2 extends keyof T[K1]>(outer: K1, inner: K2): [K1,K2]
function keys(): string[]{
return [...arguments];
}
return keys
}