0

I'm trying to create a Generic Forward Ref component that accepts a value of generic type and a render function that also needs a value property from that same generic type.

I get the Typescript error: TS2322: Type 'Product' is not assignable to type 'T'.   'Product' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Draggable'. (L34 @ https://github.com/pieterjandebruyne/generic-forward-ref-issue/blob/master/src/GenericForwardRefComponent.tsx)

I already found some posts on stack overflow explaining what and why it could go wrong but still I feel that none of the explanations really apply to this usecase..

I created a public repo to recreate the issue @ https://github.com/pieterjandebruyne/generic-forward-ref-issue

Files that are being used are:

  • App.tsx -> calls the GenericForwardRefComponent
  • GenericForwardRefComponent.tsx -> should render the ProductRow and pass the generic type
  • Interfaces.ts -> all types
  • ProductRow.tsx -> component that should be rendered

The ultimate goal is that I could also have for example a CategoryRow (with value of type Category (that has id: string)) that I could pass to the GenericForwardRefComponent

Is this something that would be possible or am I just trying to stretch the current limits of React/TS? Also I would love to hear an explanation why this error occurs as I don't see the error that could happen with this implementation. This stackoverflow probably addresses this but I can't figure out why/which part triggers it in my code.. How to fix TS2322: "could be instantiated with a different subtype of constraint 'object'"?

I used the generic forward ref approach I found @ https://dirask.com/posts/React-forwardRef-with-generic-component-in-TypeScript-D6BoRD

1 Answers1

0

You can see that with this inside the function with this header

const x = <T extends Draggable>(
        {
            dragOverlay,
            renderItem,
            value
        }: GenericForwardedRefComponentProps<T>,
        reference: ForwardedRef<HTMLDivElement>
    )

you call this function:

        return renderItem({
            value,
            reference
        })

If i simplify, the renderItem has type (value: T) => ... but then in the body, the type of value is always Product.

When the function would be called with T = Product, that would work fine, but consider, that the caller of this function can call the function with T = MyType like this :

interface MyType extends Draggable {
    my_prop: string
} 
x<MyType>({ renderItem: (x: MyType) => { x.my_prop }, value: { name: "XXX" } })

You can see, that in the function renderItem I'm accessing a field, that is not present in the product type. That's why there is this error. I have fulfilled the contract of the function, but the result is that I'm accessing missing fields.

Anyway, the function is called only once, so you can remove the type parameter and like this:

    (
        {
            dragOverlay,
            renderItem,
            value
        }: GenericForwardedRefComponentProps<Product>,
        reference: ForwardedRef<HTMLDivElement>
    ) => {

Then, typescript will be satisfied.

ryskajakub
  • 6,351
  • 8
  • 45
  • 75
  • Thank you for the explanation, I see how typescript would be satistied when hardcoding the for GenericForwardedRefComponentProps but my whole objective was to make this generic.. I was able to pinpoint the issue now, being `export interface GenericForwardedRefComponentProps extends ComponentToRenderProps` instead of `export interface GenericForwardedRefComponentProps extends GenericComponentToRenderProps`. I pushed a "Solution" commit to the demo branch. Any way you could include this into your answer? Would love to give you credit for it – Pieter-Jan De Bruyne Dec 02 '22 at 11:52