Some experimentation with typescript as presented me with a challenge: can you define a type which takes any form of sequential key object, analogous to a path in a filesystem, and uses it to recursively look up keys of an interface?
An example:
interface someDeeplyNestedInterface {
a: {
b: {
c: {
d: {
e: any
}
}
}
}
}
}
The answer, like all things TypeScript is yes you can. Here's my solution:
type nonEmptyObject = {[k in keyof any]: any};
type _ObjPath<Obj, Key extends keyof Obj> = Obj extends nonEmptyObject ? [Key] | [Key, ...ObjPath<Obj[Key]>] : [Key];
type ObjPath<Obj> = _ObjPath<Obj, keyof Obj>;
Interestingly, this code won't compile, as the TSC warns about exceedingly, possibly infinite depth in the types - rightly so. I can confirm this snippet works as this compiled in the playground briefly, although I can't seem to reproduce it. Hence my question; Is there a way to assert that a type is not infinite?
My thinking being, since this type always reads the keys of an object, if the given type Obj
is not an object, there are no keys to be read, hence the recursive type reaches its terminal condition (? ... : [Key]
). Understandably, this is difficult to infer programatically, so the question is a) whether there is a way to rewrite the type to avoid such errors, or b) assert that the type is not infinite, and the compiler should step through it without regard for depth?
I understand that there is a potential XY problem here, however I'd like to emphasise that this code is not meant to be functional, rather an experiment of what myself as a programmer and the compiler are capable of. Avoiding why are you trying to do this questions would be greatly appreciated.