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.
ts
import {createModel } from '@rematch/core'import type {RootModel } from './models'Âexport constcount =createModel <RootModel >()({state : 0,reducers : {increment (state ,payload : number) {returnstate +payload },},effects : (dispatch ) => ({incrementAsync (payload : number,state ) {dispatch .count .increment (payload )},}),})
ts
import {createModel } from '@rematch/core'import type {RootModel } from './models'Âexport constcount =createModel <RootModel >()({state : 0,reducers : {increment (state ,payload : number) {returnstate +payload },},effects : (dispatch ) => ({incrementAsync (payload : number,state ) {dispatch .count .increment (payload )},}),})
In the case of a complex state, you can just type the state with the as
keyword:
ts
import {createModel } from '@rematch/core'import type {RootModel } from './models'ÂtypeNames = 'custom'typeComplexCountState = {count : numbermultiplierName :Names }Âexport constcount =createModel <RootModel >()({state : {count : 0,multiplierName : 'custom',} asComplexCountState ,reducers : {increment (state ,payload : number) {return {count :state .count +payload ,multiplierName : 'custom',}},},effects : (dispatch ) => ({incrementEffect (payload : number,rootState ) {dispatch .count .increment (payload )},}),})
ts
import {createModel } from '@rematch/core'import type {RootModel } from './models'ÂtypeNames = 'custom'typeComplexCountState = {count : numbermultiplierName :Names }Âexport constcount =createModel <RootModel >()({state : {count : 0,multiplierName : 'custom',} asComplexCountState ,reducers : {increment (state ,payload : number) {return {count :state .count +payload ,multiplierName : 'custom',}},},effects : (dispatch ) => ({incrementEffect (payload : number,rootState ) {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.tsts
// @filename: models.tsimport {Models } from "@rematch/core"import {count } from "./count"Âexport interfaceRootModel extendsModels <RootModel > {count : typeofcount }Âexport constmodels :RootModel = {count }
models.tsts
// @filename: models.tsimport {Models } from "@rematch/core"import {count } from "./count"Âexport interfaceRootModel extendsModels <RootModel > {count : typeofcount }Âexport constmodels :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.tsts
import {init ,RematchDispatch ,RematchRootState } from '@rematch/core'import {models ,RootModel } from './models'Âexport conststore =init ({models ,})Âexport typeStore = typeofstore export typeDispatch =RematchDispatch <RootModel >export typeRootState =RematchRootState <RootModel >
store.tsts
import {init ,RematchDispatch ,RematchRootState } from '@rematch/core'import {models ,RootModel } from './models'Âexport conststore =init ({models ,})Âexport typeStore = typeofstore export typeDispatch =RematchDispatch <RootModel >export typeRootState =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.tsts
// @filename: store.ts
store.tsts
// @filename: store.ts
React Hooks Types​
- RootState and Dispatch types: import this types from the previous file mentioned: init
useSelector​
tsx
importReact from 'react'import {RootState } from './store'import {useSelector } from 'react-redux'ÂconstCount = () => {constcountState =useSelector ((state :RootState ) =>state .count )Âreturn <div >example</div >}
tsx
importReact from 'react'import {RootState } from './store'import {useSelector } from 'react-redux'ÂconstCount = () => {constcountState =useSelector ((state :RootState ) =>state .count )Âreturn <div >example</div >}
useDispatch​
tsx
importReact , {useEffect } from 'react'import {Dispatch } from './store'import {useDispatch } from 'react-redux'ÂconstCount = () => {constdispatch =useDispatch <Dispatch >()useEffect (() => {dispatch .count .incrementAsync (2)}, [])Âreturn <div >example</div >}
tsx
importReact , {useEffect } from 'react'import {Dispatch } from './store'import {useDispatch } from 'react-redux'ÂconstCount = () => {constdispatch =useDispatch <Dispatch >()useEffect (() => {dispatch .count .incrementAsync (2)}, [])Âreturn <div >example</div >}
React class types​
tsx
importReact from 'react'import {RootState ,Dispatch } from './store'import {connect } from 'react-redux'ÂclassApp extendsReact .PureComponent <Props > {render () {const {countState } = this.props return <div >example</div >}}ÂconstmapState = (state :RootState ) => ({countState :state .count ,})ÂconstmapDispatch = (dispatch :Dispatch ) => ({count :dispatch .count ,})ÂtypeStateProps =ReturnType <typeofmapState >typeDispatchProps =ReturnType <typeofmapDispatch >typeProps =StateProps &DispatchProps Âexport defaultconnect (mapState ,mapDispatch )(App )
tsx
importReact from 'react'import {RootState ,Dispatch } from './store'import {connect } from 'react-redux'ÂclassApp extendsReact .PureComponent <Props > {render () {const {countState } = this.props return <div >example</div >}}ÂconstmapState = (state :RootState ) => ({countState :state .count ,})ÂconstmapDispatch = (dispatch :Dispatch ) => ({count :dispatch .count ,})ÂtypeStateProps =ReturnType <typeofmapState >typeDispatchProps =ReturnType <typeofmapDispatch >typeProps =StateProps &DispatchProps Âexport defaultconnect (mapState ,mapDispatch )(App )
Effects returning values​
There's a situation where if you're accessing the rootState
value of the same model and returning this value. TypeScript will fail because circular references itself (has sense)...
You should try to avoid returning values on effects and just dispatch data to reducers or write pure functions outside Rematch for a better performance.
Anyways, you can omit this error force typing the effect. Related Github Issue
Instead of:
ts
import {createModel } from '@rematch/core'import {RootModel } from './models'Âexport constType of property 'count' circularly references itself in mapped type '{ [x: string]: any; count: any; }'.2615Type of property 'count' circularly references itself in mapped type '{ [x: string]: any; count: any; }'.count =createModel <RootModel >()({state : 0,reducers : {increment (state ,payload : number) {returnstate +payload },},effects : (dispatch ) => ({asyncincrementAsync (payload : number,state ) {dispatch .count .increment (payload )returnstate .count },}),})
ts
import {createModel } from '@rematch/core'import {RootModel } from './models'Âexport constType of property 'count' circularly references itself in mapped type '{ [x: string]: any; count: any; }'.2615Type of property 'count' circularly references itself in mapped type '{ [x: string]: any; count: any; }'.count =createModel <RootModel >()({state : 0,reducers : {increment (state ,payload : number) {returnstate +payload },},effects : (dispatch ) => ({asyncincrementAsync (payload : number,state ) {dispatch .count .increment (payload )returnstate .count },}),})
Define the return value:
ts
import {createModel } from '@rematch/core'import {RootModel } from './models'Âexport constcount =createModel <RootModel >()({state : 0,reducers : {increment (state ,payload : number) {returnstate +payload },},effects : (dispatch ) => ({asyncincrementAsync (payload : number,state ):Promise <number> {dispatch .count .increment (payload )returnstate .count },}),})
ts
import {createModel } from '@rematch/core'import {RootModel } from './models'Âexport constcount =createModel <RootModel >()({state : 0,reducers : {increment (state ,payload : number) {returnstate +payload },},effects : (dispatch ) => ({asyncincrementAsync (payload : number,state ):Promise <number> {dispatch .count .increment (payload )returnstate .count },}),})