I am trying to create a generic type that when parametrized with some object type, will form a new type allowing only any path that is present in the object type. I have adapted my solution from here: How to match nested Keys in Typescript
While this solution works for objects, it does not behave properly for tuples, when the tuple is not top-level. Why is this?
I believe the answer may lie in my lack of understanding of mapped types and typescript types, specifically with regard to the behavior of indexing and keyof. The only place I have been able to find documentation of the combination of these two things together is here in the docs (cmd+f "[keyof").
I have seen a similar answer that would mostly accomplish my goal, but at this point I'm in a bit of a bloodlust and just want to understand why my current solution is failing. That other answer is a whole other can of worms: I have found that if you change ...keys to keys, it stops working, which confuses me even further. - These seems to be due to type inference.
// Note: using 4.1.3
// Expect this to take an object and produce as a type the paths that are accessible in the object
type Path<V> = {
[K in keyof V]:
Path<V[K]> extends any[]
? [K, ...Path<V[K]>]
: never
| [K]
| []
}[keyof V]
// Example objects
type InputArrayType = [
string,
string,
string
]
type InputNestedDictType = {
a: {
aa: string
},
b: {
ba: string
}
}
type InputNestedArrayType = [
[
string,
string
],
[
string,
string
], {
a: string
}
]
// Expectations that are working properly:
const path0: Path<InputArrayType> = ["0"]; // okay
const path1: Path<InputArrayType> = ["3"]; // expected type error
const path2: Path<InputNestedDictType> = ["a"] // okay
const path3: Path<InputNestedDictType> = ["a","aa"] // okay
const path4: Path<InputNestedDictType> = ["a","ba"] // expected type error
const path5: Path<InputNestedDictType> = ["z"] // expected type error
const path6: Path<InputNestedArrayType> = ["0"] // okay
const path7: Path<InputNestedArrayType> = ["2","a"] // okay
// Expectations that are not working properly:
const path8: Path<InputNestedArrayType> = ["0","0"] // Unexpected type error