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 inferD
using 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 thenR
will be constrained toRecursive<string>
, which breaks things.My suggestion is to remove
D
entirely, 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
D
can be computed fromkeyof R
(it should be just thosestring
keys ofR
which are not also keys ofRec
). And you can use it as desired:Playground link to code