Home > Mobile >  React and Typescript: Can I overload a function with a different number of generics?
React and Typescript: Can I overload a function with a different number of generics?

Time:12-02

I am writing a React functional component using typescript.To format the data before submitting it, I want to create a Form component that optionally has a format function, and a FormattedValue generic type.

However, it shouldn't allow you to use format without setting the generic type, or else you could accidentally submit the form with the wrong type.

I can't figure out how to make a function overload so that the Form function either has the generic FormattedValue set and the format parameter, or none of them.

This is a minimal example of what I tried to do:

import {Form} from "../components/Form"

type FormProps<Value> = {
    submit: (data: Value) => void;
}

type FormattedFormProps<Value, FormattedValue> = {
    submit: (data: FormattedValue) => void;
    format: (value: Value) => FormattedValue
}

function GenericForm<V>({submit}: FormProps<V>): ReactNode;
function GenericForm<V,F>({submit, format}: FormattedFormProps<V, F>) {
  return (
    <Form 
     //...
    onSubmit={(value) => {
        submit(format ? format(value) : value)
    }}
    >
     // ...
    </Form>
  )
}

export default GenericForm

This gives the error: This overload signature is not compatible with its implementation signature.(2394).

I want to avoid having to create a separate component for a normal form and a form with formatting.

CodePudding user response:

However, it shouldn't allow you to use format without setting the generic type, or else you could accidentally submit the form with the wrong type.

I disagree with this because FormattedFormProps will ensure that the output of format is the input of submit. You don't need 2 types, you can simply use that one for both, by making format optional

type FormProps<Value, FormattedValue> = {
    submit: (data: FormattedValue) => void;
    format?: (value: Value) => FormattedValue
}

function GenericForm<V, F>({submit, format}: FormProps<V, F>) {
  return (
    <Form 
     //...
    onSubmit={(value) => {
        submit(format ? format(value) : value)
    }}
    >
     // ...
    </Form>
  )
}

// infer FormProps<unknown, number>
const OK1 = GenericForm({submit: (data: number) => {}})

// infer FormProps<string, number>
const OK2 = GenericForm({submit: (data: number) => {}, format: (a: string) => 42})

// infer FormProps<string, number>
const ERROR = GenericForm({submit: (data: number) => {}, format: (a: string) => 'foo'})
//                                                       ~~~~~~
// Type '(a: string) => string' is not assignable to type '(value: string) => number'.
//  Type 'string' is not assignable to type 'number'.(2322)

If you want for some reason to restrict the types that must be used for formatting and submitting on the call site you can create variants of GenericForm without re-implementing it, like so:

const FormStringToNumber = GenericForm<string, number>;
  • Related