#Polymorphic component causing "could be instantiated with a different subtype of constraint"

12 messages · Page 1 of 1 (latest)

patent skiff
#

waves I'm trying to create a polymorphic react-select wrapper, and am running into 'boolean' is assignable to the constraint of type 'Multi', but 'Multi' could be instantiated with a different subtype of constraint 'boolean'., where Multi is Multi extends boolean = false, which I do not understand even after doing a bunch of Googling (I'm convinced I'm setting the defaults in the right places and not shadowing anything, but here we are so clearly I'm not).

lucid flower
#

Just to explain what <Multi extends boolean = false> means. You can have a generic type:

type Foo<X extends boolean> { prop: X };

And you can use it as follows

const a: Foo<true> = { prop: true; } // a.prop is of type "true"
const b: Foo<false> = { prop: false; } // b.prop is of type "false"
const c: Foo<boolean> = { prop: foo; } // c.prop is of type boolean

// But you can't omit the type
const d: Foo = ... // error: Generic type 'Foo' requires 1 type argument.

Now, if you want to be able to omit the type argument, you can give it a default value. That's where the <... = false> comes in. If you defined the type as

type Foo<X extends boolean = false> { prop: X };

// Then if you write
const d: Foo = { prop: false } // Because you've omitted the generic type arg, X is defaulted to `false` so d.prop is of type "false"
#

This is an excellent write up on the cause of your exact error message
https://stackoverflow.com/questions/56505560/how-to-fix-ts2322-could-be-instantiated-with-a-different-subtype-of-constraint

Although it's a little over my head to see how this applies to your case. What I think is happening is you're typing an argument as <Multi extends boolean>, which may resolve to true, but you're assigning it to something which is generically typed as <Multi extends boolean>. Once it's been resolved to true it can't be assigned to something that accepts boolean.

In essence the Multi on line 31, is a different generic type from the Multi on line 25.

patent skiff
#

Yeah, I reached the same conclusion but cannot for the life of my figure out how to do it correctly

#

Admittedly I don't think the default here is at fault, removing all the defaults result in the same error still, even thought they're all typed as boolean and the generics are being passed around everywhere

vapid inlet
#

@patent skiff I haven't looked closely, but in general, it's not really possible to do a generic component with forwardRef without just casting.

#

I'd figure out the type you want the external contract to look like and just cast whatever comes out of forwardRef.

#

(And look forward to React 19 making forwardRef obsolete)

patent skiff
#

Oh interesting, we've definitely make generics work with forwardRef before

vapid inlet
#

I think the main thing you really can't do is pass a generic function into forwardRef and have a generic function come out.