Typescript

Rematch handles Typescript inference practically out of the box, we have all our codebase with Typescript (latest version) and we do continuous testing to our typescript examples.

For getting a cool Typescript setup with Rematch, it's as easy as using createModel helper.

createModel#

Use helper method createModel to create a model.

count.ts
import { createModel } from '@rematch/core'
import { RootModel } from './models'
export const count = createModel<RootModel>()({
state: 0,
reducers: {
increment(state, payload: number) {
return state + payload
},
},
effects: (dispatch) => ({
incrementAsync(payload: number, state) {
dispatch.count.increment(payload)
},
}),
});

In the case of a complex state, like custom types just use as:

import { createModel } from '@rematch/core'
import { RootModel } from './models'
type Names = 'custom'
type ComplexCount = {
count: number;
multiplierName: Names;
}
export const count = createModel<RootModel>()({
state: {
count: 0,
multiplierName: 'custom'
} as ComplexCount, // <-
reducers: {
increment(state, payload: number) {
return {
count: state.count + payload
}
},
},
effects: (dispatch) => ({
incrementAsync(payload: number, state) {
dispatch.count.increment(payload)
},
}),
});

RootModel#

RootModel is the file that stores all your models. We need it because you can dispatch effects and access state from other models (it's global), so we need to know all the models for bringing you the intellisense.

models.ts
import { Models } from '@rematch/core'
import { count } from './count'
export interface RootModel extends Models<RootModel> {
count: typeof count
}
export const models: RootModel = { count }

init() store#

init#

With your model ready with createModel() helper and the RootModel exported, you only need to init() the store.

Now we like to export some common types:

  • Store: type
  • RematchDispatch: useful for knowing all the effects and reducers methods and his parameters
  • RematchRootState: you will get intellisense of each state of each model.
store.ts
import { init, RematchDispatch, RematchRootState } from '@rematch/core'
import { models, RootModel } from './models'
export const store = init({
models,
})
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel>
tip

In the case you use some plugin, please read this:

init with plugins#

Some plugins modifies the store like @rematch/loading, that introduces a new state with all your promises status, Typescript to know that needs some helper.

You need to pass the RootModel to init() function and introduce the helpers:

  • @rematch/loading: { ExtraModelsFromLoading }
  • @rematch/updated: { ExtraModelsFromUpdated }
store.ts
import { init, RematchDispatch, RematchRootState } from '@rematch/core'
import { models, RootModel } from './models'
/** Plugins **/
import updatedPlugin, { ExtraModelsFromUpdated } from '@rematch/updated'
import loadingPlugin, { ExtraModelsFromLoading } from '@rematch/loading'
type FullModel = ExtraModelsFromLoading<RootModel> & ExtraModelsFromUpdated<RootModel>
export const store = init<RootModel, FullModel>({
models,
plugins: [
loadingPlugin(),
updatedPlugin(),
]
})
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel>

React Hooks Types#

  • RootState and Dispatch types: import this types from the previous file mentioned: init
import React from 'react'
import { RootState, Dispatch } from './store'
import { useDispatch, useSelector } from 'react-redux'
const Count = () => {
const countState = useSelector((state: RootState) => state.count)
const dispatch = useDispatch<Dispatch>()
return (
<div>example</div>
)
}

React class types#

import React from 'react'
import { RootState, Dispatch } from './store'
import { connect } from 'react-redux'
class App extends React.PureComponent<Props> {
render() {
const { countState } = this.props
return (
<div>example</div>
)
}
}
const mapState = (state: RootState) => ({
countState: state.count,
})
const mapDispatch = (dispatch: Dispatch) => ({
count: dispatch.count,
})
type StateProps = ReturnType<typeof mapState>
type DispatchProps = ReturnType<typeof mapDispatch>
type Props = StateProps & DispatchProps
export default connect(mapState, mapDispatch)(App)