Dan Lincoln
Dan Lincoln

Reputation: 21

Defining a generic based on react prop

I'm trying to define a generic type based on the type of a prop. The implementation looks something like

interface ChildrenProps<T> {
  values: T;
  errors: T;
}

interface FormHandlerProps<T> {
  initialValues: T;
  children: React.FC<ChildrenProps<T>>;
}

export function FormHandler<T>({
  children,
  initialValues,
}: FormHandlerProps<typeof initialValues>) {
  const [values] = useState<typeof initialValues>(initialValues)

  return(
     <>
        children({values})
     </>
  )

}

However when I implement as below, 'values' is defined as 'any'

<FormHandler
    initialValues={{name: 'test', address: 'test'}}
>

{({values}) => (
    <p> {values.name} </p>
    <p> {values.address} </p>
    <p> {values.foobar} </p> // SHOULD BE INVALID
)}


</FormHandler>

Upvotes: 0

Views: 45

Answers (1)

jcalz
jcalz

Reputation: 328262

You can't define the type of initialValues in terms of typeof initialValues; the compiler sees that as circular and doesn't know what to do. It bails out by giving it the any type:

export function FormHandler<T>({
    children, initialValues
    // -----> ~~~~~~~~~~~~~
    // 'initialValues' implicitly has type 'any' because it does not have a type 
    // annotation and is referenced directly or indirectly in its own initializer.
}: FormHandlerProps<typeof initialValues>) { /* ... */ }

You should have seen a compiler warning to this effect as shown above, assuming you are using the --noImplicitAny or --strict compiler options. If you're not, you really should consider it, since it does a good job of catching errors.


Anyway, the fix here is to just give the argument an explicit type that is not self-referential:

export function FormHandler<T>({
    children, initialValues
}: FormHandlerProps<T>) {
    const [values] = useState<typeof initialValues>(initialValues)

    return (
        <>
            children({values})
        </>
    )
}

And then the rest of your code should hopefully work as expected.

Playground link to code

Upvotes: 2

Related Questions