Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React prop types with TypeScript - how to have a function type?

Is there a best practice for having a function type with React prop types with TypeScript?

I assumed this would work but in fact it errored:

type Props = {
  onClick: Function
};

const Submit = ({ onClick }: Props) => {
  return (
    <button type="button" onClick={onClick}>
      click me
    </button>
  );

Following this thread I got it working, but it seems needlessly verbose: https://github.com/Microsoft/TypeScript/issues/20007

type Props = {
  onClick: (...args: any[]) => any;
};

const Submit = ({ onClick }: Props) => {
  return (
    <button type="button" onClick={onClick}>
      click me
    </button>
  );
like image 404
Evanss Avatar asked Aug 15 '19 13:08

Evanss


People also ask

How do you pass a function as a prop in TypeScript?

To pass a function as props in React TypeScript: Define a type for the function property in the component's interface. Define the function in the parent component. Pass the function as a prop to the child component.

How do you give types of props in React?

To run typechecking on the props for a component, you can assign the special propTypes property: import PropTypes from 'prop-types'; class Greeting extends React. Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } Greeting. propTypes = { name: PropTypes.

Should we use PropTypes with TypeScript?

Typescript and PropTypes serve different purposes. Typescript validates types at compile time, whereas PropTypes are checked at runtime. Typescript is useful when you are writing code: it will warn you if you pass an argument of the wrong type to your React components, give you autocomplete for function calls, etc.


2 Answers

Concerning Function and best practices

Function in practice has unsufficient type-safety, being the super-type of all functions. You better replace it with a function type - see the "Solutions" section down under.

In above example, Function is not assignable to the more narrow onClick function type, which causes the error at hand (Playground example).

In addition to mentioned issue, here is what TypeScript docs say about Function:

This is an untyped function call and is generally best avoided because of the unsafe any return type. If need to accept an arbitrary function but don’t intend to call it, the type () => void is generally safer.

typescript-eslint has discarded Function with the ban-types rule, emitting following message with default configuration (see also here):

The Function type accepts any function-like value. It provides no type safety when calling the function, which can be a common source of bugs. If you are expecting the function to accept certain arguments, you should explicitly define the function shape.

Better solutions

React already comes with built-in event handler-types to handle common events.

For example click (Playground):
type Props = {
  onClick: React.MouseEventHandler<HTMLButtonElement>
};

const Submit = ({ onClick }: Props) => <button onClick={onClick}> click </button>
A more general alternative is to use function types as follows:
type Props = { 
  onClick: (event: React.MouseEvent<HTMLElement>) => void
};

void is more restrictive than any. There is no chance to return a value in the callback by accident, which would be possible with any.

In summary, we embrace typing capabilities of TypeScript instead of using Function or any. The parameter now is known to be MouseEvent and the return type void, identifying it as a callback.

Related

Typescript: How to define type for a function callback (as any function type, not universal any) used in a method parameter

like image 110
ford04 Avatar answered Sep 28 '22 23:09

ford04


You can simply write it like this, which prevents any and is more telling. Especially for custom functions:

Interface Props { onClick: (e: Event) => void; }

This will tell the calling component, what onClick will expect and what the parameters are.

Hope this helps. Happy coding.

like image 28
Domino987 Avatar answered Sep 29 '22 01:09

Domino987