Typing React with typescript
Tags: react, typescript, redux, materialui
Published: 2019-05-18 20:22:16

TypeScript has really taken off lately in the React world and rightly so, it's a great way to keep your code documented and help you keep some errors at bay.

I have worked with TypeScript in a few projects the last few years, all involving Angular in some form but never with React so I had some new challenges in front of me when I started my current project with React and TypeScript.


##Functional components##

Let us start off easy with a basic functional component:

```javascript
interface OwnProps {
  myProp:string;
}

const MyButton: React.FC<OwnProps> = (props) => {
  return (<button />);
}

export default MyButton as React.ComponentType<OwnProps>;
```

We create an interface called `OwnProps` where we define all props we want the component to have. 

`OwnProps` is then used to define the component: `React.FC<OwnProps>` as well as when we export the component `as React.ComponentType<OwnProps>` to clearly signal what props are available.

In this basic example it may seem unnecessary but, as we shall see further down, when components get more complex it will save us some headache.
This approach will also help negate the following error:

> error TS2604: JSX element type 'MyComponent' does not have any construct or call signatures.

##Wrapping another component##

In some cases you might want to wrap another component and include that components interface in your own. This is usually the case when working with base components from a library and since we use Material UI (MUI for short)as a base component library I will use that in the example.

```javascript
interface OwnProps {
  buttonText:string;
}
type Props = OwnProps & ButtonProps;

const MyButton: React.FC<Props> = ({buttonText, ...rest}) => {
  return (<Button {...rest}>{buttonText}</Button>);
};

export default MyButton as React.ComponentType<Props>;
```

The `Props` type can be seen as the sum of all parts that the component will consist of. In this case we want to use `ButtonProps` from MUIs Button component and merge it with our own and expose both props to the consumers of the component. 

This is still not very advanced but since we use MUI we also use JSS for styling so let us add that to the mix!


##Using WithStyles and WithTheme##

Since we use MUI we handle styling with JSS and the generated CSS classes are injected via the `withStyles` HOC. This caused some issues since a classes object containing the class names is injected into your props and to use classes you would need to include that object in your prop type.

Luckily we have the WithStyles type to help us! 
`WithStyles<typeof styles>` takes a generic type argument of your style object so you don't have to worry about it keeping your types DRY.

[The typescript section at Material UI](https://material-ui.com/guides/typescript/) explains the problems with withStyles more in detail, ready up on it if you plan to use MUI and TypeScript.


```javascript
const styles: (theme: Theme) => StyleRules<string> = theme =>
  createStyles({
    root: {
      margin: theme.spacing.unit
    }
  });
interface OwnProps {
  buttonText:string;
}
type PublicProps = OwnProps & ButtonProps;
type Props = PublicProps & WithStyles<typeof styles>;
const MyButton: React.FC<Props> = ({classes, buttonText, ...rest}) => {
  return (
    <Button {...rest} className={classes.root}>
      {buttonText}
    </Button>);
};
export default withStyles(styles)(MyButton) as React.ComponentType<PublicProps>;

```

The addition we've done here is adding a `PublicProps` type and using that instead of the Props type when exporting the component. This is of course because we also want to use WithStyles but not expose it to anyone using the button.

Had we used the Props type instead of PublicProps we would get a pesky TypeScript error complaining about classes property missing.

![TS2741: Property 'classes' is missing in type '{buttonText: string}' but required in type '{ classes: Record<string, string>}'](https://thepracticaldev.s3.amazonaws.com/i/yzbeicf42yxovopp1zfl.PNG)



##Redux connect and compose##

But what would React be without state handling? We use Redux for this so let us connect MyButton and get the buttonText prop from state instead.


```javascript
const styles: (theme: Theme) => StyleRules<string> = theme =>
  createStyles({
    root: {
      margin: theme.spacing.unit
    }
  });

interface StateProps {
  buttonText:string
}
interface DispatchProps {
  dispatch: ThunkDispatch<IAppState, undefined, Action>;
}
interface OwnProps {}
type PublicProps = OwnProps & ButtonProps;
type Props = PublicProps & 
             DispatchProps & 
             StateProps & 
             WithTheme &
             WithStyles<typeof styles>;

const MyButton: React.FC<Props> = ({classes, buttonText, ...rest}) => {
  return (
    <Button {...rest} className={classes.root}>
      {buttonText}
    </Button>);
};

const mapStateToProps = (state: IAppState): StateProps => {
  return {
    buttonText: state.buttonText
  };
};

export default compose(
  withStyles(styles, { withTheme: true }),
  connect<StateProps, DispatchProps, OwnProps, IAppState>(mapStateToProps)
)(MyButton) as React.ComponentType<PublicProps>;

```

We have not started using hooks for our state so we go with the good old connect. Since we now use both `connect` and `withStyles` we need to use `compose` to merge them.

We create `StateProps` as return type of `mapStateToProps` and `DispatchProps` which types the dispatch function that is returned by default if we don't add a `mapDispatchToProps` function. In out case we use Thunk so if you use some other tool you need to use that instead.

I have also added an example of using the MUI theme in the component, this is done by adding `{ withTheme: true }` as a second argument to `withStyles`. 
This will inject a theme property into your props so we need to specify that in our `Props` type using `WithTheme` from MUI.


##Bonus: typing the useState hook##

Not really rocket science but good to know!

```javascript
const [state, setState] = useState<string>("Hello world!")`
```

##End note##

TypeScript can be very frustrating when starting out with many errors that are not clear so I hope this article could be of some help or inspiration, it took us a while to settle on a TypeScript pattern that worked for us and helped us mitigate most of the errors that might occur when working with different libraries.

Feel free to leave any suggestions in the comment field.

[A gist of the code examples are available at GitHub.](https://gist.github.com/fredrikbergqvist/dcff13628f9ebe3fdc2d28c67f24921c)