Closed
Description
Prior Issues
- fix replaceReducer with a store enhancer #3524
- TypeScript defintion of StoreEnhancer does not allow state extension with replaceReducer #3482
What is the current behavior?
Code
// Here's the original reducer
type CounterAction = { type: 'INCREMENT' } | { type: 'DECREMENT' };
function counter(state = 0, action: CounterAction) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
const originalStore = createStore(counter);
// Here's a new reducer
type StringCaserAction = { type: 'UPPER_CASE' } | { type: 'LOWER_CASE' };
function stringCaser(state = 'testing', action: StringCaserAction) {
switch (action.type) {
case 'UPPER_CASE':
return state.toUpperCase();
case 'LOWER_CASE':
return state.toLowerCase();
default:
return state;
}
}
const newStore = originalStore.replaceReducer(stringCaser);
// ok
newStore.dispatch({ type: 'UPPER_CASE' });
// I can still use the old store ?!?
// But the old store has been mutated and now has the new reducer
// The state is incorrectly typed as a number here
// It will actually return a string because the reducer has been replaced
const myNumber = originalStore.getState();
Output
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
const originalStore = createStore(counter);
function stringCaser(state = 'testing', action) {
switch (action.type) {
case 'UPPER_CASE':
return state.toUpperCase();
case 'LOWER_CASE':
return state.toLowerCase();
default:
return state;
}
}
const newStore = originalStore.replaceReducer(stringCaser);
// ok
newStore.dispatch({ type: 'UPPER_CASE' });
// I can still use the old store ?!?
// But the old store has been mutated and now has the new reducer
// The state is incorrectly typed as a number here
// It will actually return a string because the reducer has been replaced
const myNumber = originalStore.getState();
Compiler Options
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": 2,
"target": "ES2017",
"jsx": "React",
"module": "ESNext"
}
}
Playground Link: Provided
Steps to Reproduce
See code above.
What is the expected behavior?
There's no way to express this type of mutation in types. It would be better to remove the return type for replaceReducer
. This would then force them to cast the reducer and the store so that they realize they shouldn't be using the original store because it's types are wrong.
Environment Details
N/A