DEV Community

Cover image for React TS: Don't repeat the type when you pass functions as props, use their types.
SeongKuk Han
SeongKuk Han

Posted on • Edited on

React TS: Don't repeat the type when you pass functions as props, use their types.

Don't repeat the type when you pass functions as props, use their types.

Let's suppose, there is a component named 'SignUpForm'.



export interface SignUpFormProps {
  onSubmit?: (values: {
    username: string;
    nickname: string;
    password: string;
  }) => void;
}

export const SignUpForm = ({ onSubmit }: SignUpFormProps) => {
  const [values, setValues] = useState({
    username: "",
    nickname: "",
    password: "",
  });

  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    setValues((prevValues) => ({
      ...prevValues,
      [e.target.name]: e.target.value,
    }));
  };

  const handleSubmit: React.FormEventHandler = (e) => {
    e.preventDefault();
    onSubmit?.(values);
  };

  return (
    <form
      onSubmit={handleSubmit}
      style={{ display: "flex", flexDirection: "column" }}
    >
      <input
        type="text"
        name="username"
        value={values.username}
        onChange={handleChange}
      />
      <input
        type="text"
        name="nickname"
        value={values.nickname}
        onChange={handleChange}
      />
      <input
        type="password"
        name="password"
        value={values.password}
        onChange={handleChange}
      />
      <button type="submit">Submit</button>
    </form>
  );
};


Enter fullscreen mode Exit fullscreen mode

The submit event passes the arguments 'username', 'nickname' and 'password' to the onSubmit prop if it is there.

You can pass the function like below.



function App() {
  const handleSubmit = (
    values
  ) => {
    console.log(values);
  };

  return (
    <div>
      <SignUpForm onSubmit={handleSubmit} />
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

If you don't have an option "noImplicitAny": false, it occurs an error.

any_type_error

For avoiding this problem, you can repeat the type of the onSubmit.

repeat_type

If you just repeated like this, you'll have to keep up-to-date depending on the onSubmit prop.

It would bother you.
In this case, you can get the field type of the interface using brackets.

field_type_of_interface




function App() {
  const handleSubmit:SignUpFormProps['onSubmit'] = (
    values
  ) => {
    console.log(values);
  };

  return (
    <div>
      <SignUpForm onSubmit={handleSubmit} />
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

If there is no interface, use React.ComponentProps.

get the field type using react component props



function App() {
  const handleSubmit: React.ComponentProps<typeof SignUpForm>["onSubmit"] = (
    values
  ) => {
    console.log(values);
  };

  return (
    <div>
      <SignUpForm onSubmit={handleSubmit} />
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

That's it. I hope it will be helpful for someone.

Happy Coding!


+
Thanks for reading it, all you guys. I'm not sure if that was an appropriate example though. The point was that you can get the field type, and you can use it with other packages. Thank you for your attention again, Good coding!

Top comments (10)

Collapse
 
mrispoli24 profile image
Mike Rispoli

So I actually used to do this but now heir on the side of repeating myself and encourage the team to do the same here. One of the things that can start to happen to large typescript code bases like this is you can end up coupling lots of components together by way of their type.

By repeating the type, you may be violating DRY but you avoid making these two components dependent upon one another. It's definitely a delicate balance though.

Collapse
 
frontendtony profile image
Anthony Oyathelemhi

Or, avoid both issues altogether and define the type in a separate file and import in both components

Collapse
 
mrispoli24 profile image
Mike Rispoli

I think you end up in the same place where a bunch of components end up dependent on the same type. I do like the idea of using Omit and Partial to extend a single use case for different areas, but tread carefully with the coupling still.

Thread Thread
 
mrwensveen profile image
Matthijs Wensveen

Defining the type in a separate file is a good idea when it makes sense in your domain. In this case a Credentials type would be a good idea. If the type only matters in a specific component you'll want to avoid tight coupling, so I wouldn't export the SignupFormProps or even the onSubmit handler type.

const handleSubmit = (values: Credentials) => {
  console.log(values);
};
Enter fullscreen mode Exit fullscreen mode

Looks okay to me.

Collapse
 
torver213 profile image
Peter Kelvin Torver

Nice one. I always go with this. Don't see the reason why people are making a big deal out of this. Keeping types in a separate file and importing them where necessary makes your application obeys DRY principle.

Collapse
 
lico profile image
SeongKuk Han • Edited

Yes, that's a good idea. Thanks for your opinion :) Defining types in a seperate file and components that need the types import them.

But if you care every types like even a type of one of props, you might end up need a lot of types.

And other packages that you use might not have all types you need.

Collapse
 
duysolo profile image
Justin Phan

Yes I agree with that. Why don't we declare the type in a new file instead?

Collapse
 
lico profile image
SeongKuk Han • Edited

Yes, you're right. I agree with you. thanks for the comment :). Let's say, you make a component and use a type of another component in the component like that, they must be dependent. When the one changes, another one would be affected. If you aren't considered it, that would be not good in a way. But in the examples, I think that is totally fine. That component already depends on the SignUpForm Component. And used the field type.

Collapse
 
bobbycrooz profile image
bobbycrooz

But the interface resides inside the component to be used,
How does react knows about this before hand.

Collapse
 
lico profile image
SeongKuk Han

If the component doesn't export the interface, you can use ComponentProps.