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>;