Incorrectly infers DOM Map Instead of immutable Map inside a redux slice
Closed this issue ยท 5 comments
Bug Report
๐ Search Terms
immutable map redux slice
๐ Version & Regression Information
- This fails in some way in every version I tried, and I reviewed the FAQ for entries about
- The error changes between versions 3.7.5 and 3.8.3 (versions available on playground)
- 3.7.5 and prior can't infer the state type at all
- 3.8.3 and after infer it incorrectly
โฏ Playground Link
Playground link with relevant code
๐ป Code
// "dependencies": {
// "@reduxjs/toolkit": "^1.6.1",
// "immutable": "^4.0.0-rc.14",
// "typescript": "next"
// }
import { Map } from 'immutable';
import { createSlice } from '@reduxjs/toolkit';
// This works as you'd expect in all versions
let myMap = Map<string, number>();
myMap = myMap.set('abc', 1);
myMap = myMap.deleteAll(['abc']);
console.log(myMap);
// In a slice though the type binds to the default Map type
export const testSlice = createSlice({
name: 'test',
initialState: {
stale: Map<string, number>(),
staleLast: 0,
},
reducers: {
// <=3.7.5 errors here: Parameter 'state' implicitly has an 'any' type.(7006)
test: (state) => {
// 3.8.3+: (property) stale: globalThis.Map<string, number>
// Type 'void' is not assignable to type 'Map<string, number>'.(2322)
state.stale = state.stale.clear();
// 3.8.3+: Property 'deleteAll' does not exist on type 'Map<string, number>'.(2339)
state.stale = state.stale.deleteAll(['test']);
},
},
});
export const { test } = testSlice.actions;
export default testSlice.reducer;
๐ Actual behavior
Typescript binds the wrong Map type, using the default DOM Map instead of the immutable Map declared. It definitely knows it a Map, it just gets the wrong one.
๐ Expected behavior
That it would bind to the correct Map type.
Also note:
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"noEmit": true
},
"include": ["problem.ts"]
}
I also pushed this to a repository: https://github.com/SCdF/typescript-redux-immutable-map
This appears to be a problem with @reduxjs/toolkit
's types. It creates a WritableDraft<S>
from S
as the parameter to a reducer, and their type decides that an immutable
Map
should be represented as a regular Map
in that context.
It's not surprising to me that this doesn't work and never worked. immer
is generally expecting to receive vanilla JS objects and mixing it with another immutable library sounds complicated.
Cool since this isn't typescript I'll close this.
While I'm here are you saying that a regular Map will be immutable via immer in the context of the reducer?
That's what the @reduxjs/toolkit
types claim, but they could be wrong. I'm honestly not sure; you might want to dig in starting at https://immerjs.github.io/immer/complex-objects.
fwiw I think immer supports it but redux yells at you. I just went with type Foo = Record<string,number>
instead