0

I am doing something like the following:

import React from "react";

interface BaseFormValue {
  name: string;
}

export interface NewFormValue extends BaseFormValue {
  email: string;
}

export interface ExistingFormValue extends BaseFormValue {}

export type FormValue = NewFormValue | ExistingFormValue;

export type existingFormSetter = (
  setter: (prev: ExistingFormValue) => ExistingFormValue
) => void;

export type newFormSetter = (
  setter: (prev: NewFormValue) => NewFormValue
) => void;

export type formSetter = existingFormSetter & newFormSetter;

let newFormState: NewFormValue = {
  name: "",
  email: "",
};

const newFormSetter: newFormSetter = (setter) => {
  newFormState = setter(newFormState);
};

type NewFormProps = {
  type: "new";
  value: NewFormValue;
  onChange: newFormSetter;
};

type ExistingFormProps = {
  type: "existing";
  value: ExistingFormValue;
  onChange: existingFormSetter;
};

type FormProps = NewFormProps | ExistingFormProps;

const MyComponent: React.FC<FormProps> = (formProps) => {
  return (
    <>
      <input
        value={formProps.value.name}
        onChange={({ target: { value: input } }) => {
          // Doesn't work
          formProps.onChange((prev) => ({ ...prev, name: input }));

          // Works
          switch (formProps.type) {
            case "existing":
              formProps.onChange((prev) => ({ ...prev, name: input }));
              break;
            case "new":
              formProps.onChange((prev) => ({ ...prev, name: input }));
              break;
          }
        }}
      />
      {formProps.type === "new" && (
        <input
          value={formProps.value.email}
          onChange={({ target: { value: input } }) => {
            formProps.onChange((prev) => ({ ...prev, email: input }));
          }}
        />
      )}
    </>
  );
};

If I don't surround formProps.onChange call with an if/switch statement I get the error:

Parameter 'prev' implicitly has an 'any' type.

even if the switch does the same thing in both cases.

=================================================================================================================================

1 Answer 1

1

Your formProps.onChange is expecting either a function that takes NewFormValue and return NewFormValue or a function that takes ExistingFormValue and returns ExistingFormValue. You can't make use of that union unless you know which one you are dealing with, which is why your switch example works.

One thing that you can do is to define your update function as a generic that returns the same type as its argument. That generic signature would be assignable to both types in the union.

onChange={({ target: { value: input } }) => {
  const callback = <T extends BaseFormValue>(prev: T): T => ({...prev, name: input })
  formProps.onChange(callback);
}}

Typescript Playground Link

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.