I have a recursive type that extracts the indices from a nested object and puts them in a flat, strongly-typed tuple, like so:
type NestedRecord = Record<string, any>
type RecursiveGetIndex<
TRecord extends NestedRecord,
TPreviousIndices extends any[] = []
> = {
[K in keyof TRecord]: TRecord[K] extends NestedRecord
? RecursiveGetIndex<
TRecord[K],
AppendToTuple<TPreviousIndices,K>
>
: AppendToTuple<TPreviousIndices, K>
}[keyof TRecord]
type AppendToTuple<T extends any[], U> = [...T, U]
It works fine when called with a concrete instantiation of a nested object type, e.g.:
type AnIndex = RecursiveGetIndex<{ foo: { bar: { baz: number}}}>
In other words, as expected, AnIndex is typed as ["foo", "bar", "baz"]
But, when I try to use the Recursive type in a function, I get a recursion error:
function doSomething<T extends NestedRecord>(arg:T){
type Nested = RecursiveGetIndex<T>
const doMore = (n: Nested) => {
console.log(n) //Type instantiation is excessively deep and possibly nested
}
}
Which kind of makes sense to me because TS doesn't really know how deeply recursed T might be.
Is that correct? Why wouldn't TS just wait until it see's how doSomething is instantiated before worrying about recursion?
And, is there some way to avoid the error if I know in advance that the arg passed to the function will never exceed the TS recursion limit (50, i believe). I.e., can I somehow tell typescript that?
I've seen solutions that limit recursion basically by using a decrementing array type, e.g. this SO answer. Is that the best I can do here?
Updated
Per captain-yossarian's suggestion, the following works:
type RecursiveGetIndex<
TRecord extends NestedRecord,
TPreviousIndices extends any[] = [],
TDepth extends number = 5
> = 10 extends TPreviousIndices["length"] ? TPreviousIndices : {
[K in keyof TRecord]: TRecord[K] extends NestedRecord
? RecursiveGetIndex<
TRecord[K],
AppendToTuple<TPreviousIndices,K>
>
: AppendToTuple<TPreviousIndices, K>
}[keyof TRecord]
The error recurs, however, if this number is any higher than 10. I would have thought that should be 50. Is my type more recursive than I think?
= 10 extends TPreviousIndices["length"] ? TPreviousIndices : //etcworks, but if i make it 11 it has an error. is there some reason each level of recursion counts as more than 1 for purposes of TS's limit?