When I create a new project using the C# React/Redux Web Application template in Visual Studio, there is an error reported in the "\ClientApp\configureStore.ts" file.
"createStoreWithMiddleware(allReducers, initialState)" is underlined red and the error states:
"TS2349 (TS) Cannot invoke an expression whose type lacks a call signature. Type '{}' has no compatible call signatures."
REPRO STEPS:
I'm reluctant to simply ignore the error or suppress it, as I suspect this might be a simple case of either defining a call signature (or casting?). My Typescript knowledge is near non-existent, so apologies if this is an extremely basic question.
[UPDATE] - It turns out that if I remove the following line of code, the error is no longer displayed. Naturally this means the devTools extension won't work though, so I need to work out what this line's doing in order to get it working without throwing an error.
devToolsExtension ? devToolsExtension() : <S>(next: StoreEnhancerStoreCreator<S>) => next
The code for the "configureStore.ts" file is below - thanks in advance for any help!
import { createStore, applyMiddleware, compose, combineReducers, GenericStoreEnhancer, Store, StoreEnhancerStoreCreator, ReducersMapObject } from 'redux';
import thunk from 'redux-thunk';
import { routerReducer, routerMiddleware } from 'react-router-redux';
import * as StoreModule from './store';
import { ApplicationState, reducers } from './store';
import { History } from 'history';
export default function configureStore(history: History, initialState?: ApplicationState) {
// Build middleware. These are functions that can process the actions before they reach the store.
const windowIfDefined = typeof window === 'undefined' ? null : window as any;
// If devTools is installed, connect to it
const devToolsExtension = windowIfDefined && windowIfDefined.devToolsExtension as () => GenericStoreEnhancer;
const createStoreWithMiddleware = compose(
applyMiddleware(thunk, routerMiddleware(history)),
devToolsExtension ? devToolsExtension() : <S>(next: StoreEnhancerStoreCreator<S>) => next
)(createStore);
// Combine all reducers and instantiate the app-wide store instance
const allReducers = buildRootReducer(reducers);
const store = createStoreWithMiddleware(allReducers, initialState) as Store<ApplicationState>;
// Enable Webpack hot module replacement for reducers
if (module.hot) {
module.hot.accept('./store', () => {
const nextRootReducer = require<typeof StoreModule>('./store');
store.replaceReducer(buildRootReducer(nextRootReducer.reducers));
});
}
return store;
}
function buildRootReducer(allReducers: ReducersMapObject) {
return combineReducers<ApplicationState>(Object.assign({}, allReducers, { routing: routerReducer }));
}
Unfortunately, I have no idea where the SPA project templates are developed, so I cannot check whether this is a known problem and whether a fix for this is already in development.
However, you can easily fix this issue for your local project by yourself. The problem here is that the compose
function does not return a properly typed callable, so calling createStoreWithMiddleware
is not allowed. You can easily fix this by overwriting the type information there; there are multiple ways to do this. One would be changing the line where the error appears to the following:
const store = (<any>createStoreWithMiddleware)(allReducers, initialState) as Store<ApplicationState>;
This casts createStoreWithMiddleware
into the any
type which basically tells TypeScript that it knows nothing about it and just accepts anything. Since the result of the function call is explicitly typed using the as Store<ApplicationState>
anyway, this will not cause any problems later on either.
This is because the non-generic compose()
function returns a function with no defined parameters. The best fix for this error is to explicitly use the generic compose<T>()
function that returns the base type of StoreEnhancerStoreCreator<any>
. That type does explicitly take the two parameters you're trying to send, and calling createStoreWithMiddleware
will now have full intellisense:
const createStoreWithMiddleware = compose<StoreEnhancerStoreCreator<any>>(
applyMiddleware(thunk, routerMiddleware(history)),
devToolsExtension ? devToolsExtension() : <S>(next: StoreEnhancerStoreCreator<S>) => next
)(createStore);
// unchanged:
const store = createStoreWithMiddleware(allReducers, initialState) as Store<ApplicationState>;
Try to avoid casting something as <any>
(similar to C#'s object
type) unless that is absolutely necessary. It removes the type checking of TypeScript, which is a core reason of using TypeScript. So if a function can only ever return Cat
, Dog
or Human
, define it as Mammal
. If you later add Snakes
, you can define it as Animal
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With