1

Redux docs contain this:

import { configureStore, Action } from '@reduxjs/toolkit'
import { ThunkAction } from 'redux-thunk'

import rootReducer, { RootState } from './rootReducer'

const store = configureStore({
  reducer: rootReducer
})

if (process.env.NODE_ENV === 'development' && module.hot) {
  module.hot.accept('./rootReducer', () => {
    const newRootReducer = require('./rootReducer').default
    store.replaceReducer(newRootReducer)
  })
}

export type AppDispatch = typeof store.dispatch

export type AppThunk = ThunkAction<void, RootState, null, Action<string>>

export default store
  1. Why does the author use type instead of interface here? I've read the answer on the difference between types and interfaces but still don't get why would anyone need to use type instead of interface?
  2. What would be the interface equivalent of these lines?
export type AppDispatch = typeof store.dispatch
export type AppThunk = ThunkAction<void, RootState, null, Action<string>>
stkvtflw
  • 12,092
  • 26
  • 78
  • 155

1 Answers1

3

I'm a Redux maintainer, and I think I may have written that particular snippet.

I'm not a complete TS expert, so in some ways the whole "type vs interface" thing confuses me a bit too.

If you look at the original ThunkAction type from the redux-thunk typings, you can see that it's defined using the type keyword:

export type ThunkAction<
  TReturnType,
  TState,
  TExtraThunkArg,
  TBasicAction extends Action
> = (
  dispatch: ThunkDispatch<TState, TExtraThunkArg, TBasicAction>,
  getState: () => TState,
  extraArgument: TExtraThunkArg,
) => TReturnType;

As I understand it, a TS interface can extend another interface, but an interface can never extend a type. So, the fact thatThunkActionis defined as atypemeans thatAppThunk_must_ also be atype.

Hypothetically, you could define ThunkAction as an interface, which I think would look like:

interface ThunkAction {
    // declaring a signature here without a field name means this is describing
    // a function that matches this signature, vs an object field
    <
      TReturnType,
      TState,
      TExtraThunkArg,
      TBasicAction extends Action
    > = (
      dispatch: ThunkDispatch<TState, TExtraThunkArg, TBasicAction>,
      getState: () => TState,
      extraArgument: TExtraThunkArg,
    ) => TReturnType
}

However, in practice, it seems like declaring types of functions using interface is a rare use case. Interfaces are generally used for objects, and type for anything that's not strictly an object or is a derived type.

For AppDispatch, it's a derived type using typeof, so it has to be type.

markerikson
  • 63,178
  • 10
  • 141
  • 157