How I Use TypeScript + Redux — Part 1

mosen1
mosen1
Jul 25, 2017 · 2 min read

If you’re a relative newcomer to TypeScript like me, you might have found that documentation on how to use it with redux is fairly sparse. More experienced programmers might argue that the type definitions speak for themselves, but it’s really hard to bridge the gap from beginner to intermediate in that way.

My primary complaint was that, most of the time, generics never explained what the type parameters were meant to be. No comments were to be found, and single letter representation of generic types didn’t exactly help.


React Components

As you know, for any “container” component to make use of the redux store, it needs to make use of the connect function from react-redux.

The connect function comes in several different flavours, with different arguments for types and different numbers of arguments.

The one i use most often is the form connect<TStateProps, TDispatchProps, TOwnProps>(mapStateToProps, mapDispatchToProps): ComponentDecorator<...>

This style of connect allows you to specify props that come from your redux state, and props that are bound action creators.

Before the component class, I usually describe the shape of the properties and action creators that will be connected to the component.

First, I describe what parts of the redux state will be assigned to the component. Each reducer declares the types of all the things it holds (we’ll get to that later).

import {InstalledApplicationsState} from "../../reducers/device/installed_applications";interface ReduxStateProps {
applications?: InstalledApplicationsState;
}

This is also the interface of the value that will be returned from mapStateToProps.

Second, I describe the bound action creators that will be available on the component.

interface ReduxDispatchProps {
fetchInstalledApplications: InstalledApplicationsActionRequest
}

The definition of InstalledApplicationsActionRequest is irrelevant here, but it’s essentially a Thunk.

This is also the interface of the value that will be returned from mapDispatchToProps.

We now have enough information about each of the types to construct the component.

Note that you can declare explicit functions for mapStateToProps and mapDispatchToProps or use the arrow form of anonymous functions.

WARNING: Do not use connect() as a decorator. For some reason, TypeScript will not be able to infer the available properties on the decorated class unless you call it as a function. Decorators only seem to work if the properties of the higher order component are exactly the same as the underlying component.

All together now

export const ConnectedComponent = connect<ReduxStateProps, ReduxDispatchProps, any>(    // mapStateToProps
(state: RootState, ownProps?: any): ReduxStateProps => ({ applications: state.applications }),
// mapDispatchToProps
(dispatch: Dispatch<any>): ReduxDispatchProps => (bindActionCreators({ fetchInstalledApplications }, dispatch))
)(UnconnectedComponent);

Don’t forget that your component props interface should extend ReduxDispatchProps and ReduxStateProps OR use a union type, like so

interface DeviceApplicationsProps extends ReduxStateProps, ReduxDispatchProps, RouteComponentProps<DeviceApplicationsRouteProps> {}class UnconnectedDeviceApplications extends React.Component<DeviceApplicationsProps, undefined> {...}

TypeScript should now know that the components this.props is made up of a combination of the redux state props, dispatch props, and any other props that are passed into the component. In this example i also used RouteComponentProps from the react-router package.

In part 2 i’ll talk about typed reducers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade