I am in the following situation:
interface Rec {
key: string;
children: this[];
}
type Recursive<D extends string> = {
[K in D]: string;
} & Rec;
type FlattenRecursive =
<D extends string, R extends Recursive<D>>(rs: R[]) => Omit<R, "children">[]
const flatten: FlattenRecursive =
rs => rs.flatMap(r => flatten(r.children))
I expect the recursive call to the flatten function to be inferred as flatten<D, R> instead of the current <string, R>. Therefore, I'm forced to explicitly annotate the type arguments when calling it:
type RValueAnimal = Recursive<"value"> & { animal: string }
const rvalues: RValueAnimal[] = [
// ...
]
flatten(rvalues) // <- error
flatten<"value", RValueAnimal>(rvalues)
How can I solve this problem?
The problem with a generic function type like
is that there is no inference site for the type parameter
D. You might hope that the compiler would be able to inferDusing the generic constraint ofRecursive<D>for the type parameterR. Unfortunately, generic constraints do not serve as inference sites for other type parameters. There was a suggestion at microsoft/TypeScript#7234 to do this, but it was never implemented.That means when you call such a function, inference will fail for
D. It will therefore fall back to its own constraint ofstring, and thenRwill be constrained toRecursive<string>, which breaks things.My suggestion is to remove
Dentirely, and express the constraint you care about purely in terms ofR. For example:This is a bit convoluted, but the idea is that what you were calling
Dcan be computed fromkeyof R(it should be just thosestringkeys ofRwhich are not also keys ofRec). And you can use it as desired:Playground link to code