Search code examples
reactjstypescriptzustand

How to provide a nested Generic in typescript?


I'm using state library zustand with typescript. I want to store an object that has at least an id:number. Now I get an error, because the nested Generic is not defined correctly. How can I fix that?

Cannot find name 'T'.ts(2304)

Code

type HasId = { id: number }

export type Product<T extends HasId> = {
  product: T | null
  setProduct: (product: T | null) => void
}

export const useProductStore = create<Product<T>>(set => ({
  product: {id: 2, color: "red", // other unknown props},
  setProduct: product => {
    set({ product })
  },
}))

Solution

  • Explanation for Cannot find name 'T'.ts(2304)

    Pretty simply the reason why the compiler is throwing that error, is because there is no variable named T within your code, at least in the snippet provided

    export type Product<T extends HasId> = {
      product: T | null
      setProduct: (product: T | null) => void
    }
    

    Type Product takes an argument T which is basically of the same type as HasId

    "Translated"

    export type Product<T extends HasId> = {
       product: object with prop id and number value ({id:4}) | null
       setProduct: (product: object with prop id and number value ({id:4}) | null) => void
    }
    

    In order for the compiler to stop complaining on your scenario you would need to pass a valid argument to your type, for example:

    export const useProductStore = create<Product<{id: 2}>>(set => ({
      product: {id: 2, color: "red", // other unknown props},
      setProduct: product => {
        set({ product })
      },
    }))
    

    Since {id: 2} conforms to your generic T, the compiler is now happy.

    For your specific scenario I think the best approach would be to ditch the generic and have a type like so:

    export type Product = {
        product: HasId | null
        setProduct: (product: HasId | null) => void
    }
    

    and your function becomes

    export const useProductStore = create<Product>(set => ({
      product: {id: 2, color: "red", // other unknown props},
      setProduct: product => {
        set({ product })
      },
    }))