I'm trying to create a generic type which will consist of the argument type, and an array of itself under nodes
. My code:
type Foo<T> = T & {
nodes: Foo<T>[]
}
function Bar<A extends Foo<A>>():null{
return null;
}
When trying to run it, I'm getting
Type parameter 'A' has a circular constraint
Which I understand. The problem is, this is exactly what I want. I want to use it like so:
interface Vegetables{
id:number
nodes: Vegetables[]
}
interface Fruits{
id:number
nodes: Fruits
}
Bar<Vegetables>()
Bar<Fruits>
And I'm not sure what I'm missing
CodePudding user response:
Ok, I found a workaround.
Since we're extending, we're keeping the T
. So this should still work
type Foo<T> = {
nodes: Foo<T>[]
}
function Bar<A extends Foo<A>>():null{
return null;
}
// desired usage:
interface Vegetables{
id:number
nodes: Vegetables[]
}
interface Fruits{
id:number
nodes: Fruits[]
}
Bar<Vegetables>()
Bar<Fruits>
But if anyone has a way to make it work in the original way, please leave a response
CodePudding user response:
It is not clear what this type parameter is supposed to do, but I have the feeling you want this to fail
interface Stuff {
id:number
nodes: Vegetables[]
type: 'stuff' // adding a discriminant so that Stuff ≠ Vegetables
}
// @ts-expect-error: Stuff is not Stuff & { nodes: Stuff[] }
Bar<Stuff>()
It is not pretty, but you can do it this way
function Bar<
A extends Foo<unknown> & Check,
Check = A extends Foo<A> ? unknown: never
>():null{
return null;
}
Since unknown
is the neutral element of intersection, Check
does not affect the first type parameter when the constraint is met, and since this condition does not return A, it is not a circular constraint.
Now, you don't need to return never
when the constraint is not met, any type which is incompatible with Foo<unknown>
will be an error, so you can return some error message explaining that A
should be recursive.