Closed filiptrplan closed 2 years ago
There was one prior report of this in #1181 . Unfortunately, this really sounds like some kind of a bundling/installation problem, and not something that's actually wrong with the source code.
Given that thousands of people are using RTK with TypeScript without issues, this is probably something very specific to your project setup, and not anything we can investigate and fix ourselves.
If you can manage to put together a project that demonstrates this happening we might be able to take a look, but otherwise I suspect there's nothing we can do here.
I'm quite inexperienced in TypeScript. Can you maybe just point me in a direction where I should start troubleshooting?
I solved the error by doing the following in my tsconfig.json
This is probably just a workaround but if someone comes across this problem this should work.
{
"compilerOptions": {
"declaration": false,
},
}
@filiptrplan thanks
TLDR; No clue why this happens, but you can add immer
as a dev dependency and still compile with declarations.
Follow up on this one. I'm working with two different monorepos, both using RTK, and both using "declaration": true
. For reasons that should be obvious, simply setting "declaration": false
is not a solution (although it does work, if you don't have any need to publish types).
Here's the deal for me. One project has this error (let's call it project CRAP), the other project doesn't (call this one CLAP). I have desperately tried to figure out why this error is occurring in CRAP and not in CLAP. Nearly everything I can think of I have normalized between the two:
castDraft
from immer
, so I tried that, thinking the import might trigger some bail out logic. Didn't do squat.tsc
).immer
as a dependency or devDependency.And when I say the versions are the same, these are both running yarn workspaces, properly deduped. So I do mean the same.
The only thing that's different, that I can see at least, is the project structure and the path mappings in the tsconfigs. In CLAP, the apps themselves are not part of the path mappings (although, everything is still defined in workspaces). Only the "shared" and externally published packages are defined in the path mappings.
I frankly can't see how that would matter though. I also tried, changing the path mappings in CRAP so only the "shared" modules are mapped, and this did nothing.
All of this said, long before I tried any of this, I did find a way to "trick" TypeScript into thinking all is right in the world by adding immer
as a dev dependency. So I guess that's what I have to do? I know this "fixes" it and I can now compile with declarations and without errors, but it seems so arbitrary.
If I'm able to turn CRAP into CLAP, then I'll update this.
@ctbwx I have no idea what you're trying to say here, but explicitly typing Slice
is not how you should ever do this. Like, I can't emphasize this enough. You lose all the type inference of Name
, State
, CaseReducers
(and all the actions).
Maybe you explain how this relates to the issue here, at least.
For me, it only works if I use version 1.5.1
or earlier and I declare the precise version of the dependency in package.json
(no caret in the version string). Like @bfricka, I'm using Yarn workspaces.
export const templateSlice: any = createSlice({ name: 'template', initialState, *****
only need to declare the slice explicitly to resolve this ts type error
@PeiLiao : please don't do that :( You're throwing away all the actual TS types in templateSlice
.
Since google brought me to this one first, linking to my comment confirming a fix in the referenced issue: https://github.com/reduxjs/redux-toolkit/issues/1181#issuecomment-1183711921
Ran into this issue too - fiddling around with Immer didn't work for me, but it resolved itself once all applications in my monorepo were on the exact same (newest) version of redux-toolkit.
Hey the solution is to:
immer
to devDependenciesimport 'immer'
on top of your file, this will not import anything at runtime but it will import the types to make the compiler happyWe could fix this inside redux-library
by enforcing the type of slice.actions
to not include WritableDraft
as part of it (it's an implementation detail either way)
Adding immer
to devDependencies is not strictly necessary.
A simple import "immer"
at the top of the file quiets the error.
Hey the solution is to:
- add
immer
to devDependencies- add
import 'immer'
on top of your file, this will not import anything at runtime but it will import the types to make the compiler happyWe could fix this inside
redux-library
by enforcing the type ofslice.actions
to not includeWritableDraft
as part of it (it's an implementation detail either way)
In my case, unfortunately, none of these setups is working.
Attempts:
immer
to devDependencies and import 'immer'
on top of slice.ts fileimmer
to devDependencies and import 'immer'
on top of slice.ts fileThe only setup works when I disable declaration
in compilerOptions
. However, can't do it as the library is exportable. Are there any other settings I might miss, adding immer
. Is it possible to share a git link of your minimal?
Hey the solution is to:
- add
immer
to devDependencies- add
import 'immer'
on top of your file, this will not import anything at runtime but it will import the types to make the compiler happyWe could fix this inside
redux-library
by enforcing the type ofslice.actions
to not includeWritableDraft
as part of it (it's an implementation detail either way)In my case, unfortunately, none of these setups is working.
Attempts:
- not adding
immer
to devDependencies andimport 'immer'
on top of slice.ts file- adding
immer
to devDependencies andimport 'immer'
on top of slice.ts fileThe only setup works when I disable
declaration
incompilerOptions
. However, can't do it as the library is exportable. Are there any other settings I might miss, addingimmer
. Is it possible to share a git link of your minimal?
A solution might be adding "preserveSymlinks": true
in the "compilerOptions"
of tsconfig.json
.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { WritableDraft } from '@reduxjs/toolkit/node_modules/immer/dist/internal';
As another potential not the prettiest workaround.
Thank you @srosato this worked!
I got the same error while trying the test @reduxjs/toolkit@2.0.0-beta.0
. I'm currently working on a big monorepo and the solution was to check versions and syncpack them:
// Try this. Most likely you will have multiple versions of those packages installed.
$ yarn why redux
$ yarn why immer
The issue was that yarn does package hoisting ending up with conflicting .d.ts
files. Depending on the package version that is hoisted to the project root and your tsconfig configuration, you might end up with this type of errors.
Moving all versions to redux@5.0.0-beta.0
and immer@10.0.2
fixed this issue for me.
For me, none of the above works.
pnpm monorepo with immer
installed separately too.
Edit: Installing the same immer
version as Redux toolkit
uses, in this case it was immer@9.0.21
, and adding import "immer"
to the top of the file fixed it.
For me, none of the above works. pnpm monorepo with
immer
installed separately too.Edit: Installing the same
immer
version asRedux toolkit
uses, in this case it wasimmer@9.0.21
, and addingimport "immer"
to the top of the file fixed it.
I also recently switched to pnpm
and then I did the same as you.
What I did:
cat node_modules/@reduxjs/toolkit/package.json | grep immer
pnpm i immer@^9.0.21
// in my case redux-toolkit reported using ^9.0.21Then the error went away.
No need to my earlier workaround I posted earlier here in this thread when using yarn
, at least with pnpm
that is.
Actually, I am not sure if using import 'immer'
might be too expensive of an import, so I tried my earlier workaround and modified it with:
/* eslint-disable-next-line @typescript-eslint/no-unused-vars,unused-imports/no-unused-imports */ // noinspection ES6UnusedImports
import type { Draft } from 'immer'
The error also goes away like that on ^9.0.21
(provided the immer
version is the same as the one redux-toolkit
expects). This https://github.com/reduxjs/redux-toolkit/issues/1806#issuecomment-1536690651 worked on ^9.0.7
for me
Importing immer didn't work for me, so I referenced it as
/// <reference path="../node_modules/immer/dist/immer.d.ts" />
I came across this same error when using createEntityAdapter()
. The issue is the type of state
in the reducer. Instead of letting the type be inferred to be a WritableDraft
, you need to explicitly specify it. Based on how I fixed it in my code, I believe the code in the OP can be fixed as follows:
export interface POSState {
addedPOSItems: POSItem[],
}
const initialState = {
addedPOSItems: []
} as POSState;
export const POSSlice = createSlice({
name: 'pos',
initialState,
reducers: {
// Change `state` to `state: POSState`
// vvvvvvvvvvvvvvv
setAddedPOSItems: (state: POSState, action: PayloadAction<POSItem[]>) => {
state.addedPOSItems = action.payload;
}
}
});
In my case, I had code that looks like this:
const fooAdapter = createEntityAdapter<FooModel>();
export const fooSlice = createSlice({
name: "foo",
initialState: fooAdapter.getInitialState({
myValue: 0
}),
reducers: {
addFoo: FooAdapter.addOne,
setFoo: FooAdapter.setOne,
updateFoo: FooAdapter.updateOne,
removeFoo: FooAdapter.removeOne,
// Adding this reducer causes the error
setValue: (state, action: PayloadAction<number>) => {
state.myValue = action.payload;
}
},
});
I fixed the error by adding an interface that declared the shape of the state object returned by fooAdapter
and explicitly adding the type to state
:
export interface FooState extends EntityState<FooModel> {
myValue: number;
}
const fooAdapter = createEntityAdapter<FooModel>();
export const fooSlice = createSlice({
name: "foo",
initialState: fooAdapter.getInitialState({
myValue: 0
}),
reducers: {
addFoo: FooAdapter.addOne,
setFoo: FooAdapter.setOne,
updateFoo: FooAdapter.updateOne,
removeFoo: FooAdapter.removeOne,
setValue: (state: FooState, action: PayloadAction<number>) => {
state.myValue = action.payload;
}
},
});
This is how I solved it:
Add the same version of immer that is used in @reduxjs/toolkit as a dependency in my project, currently it's ^9.0.21
Then in ther xxx.slice.ts files top add
/// <reference types="immer" />
@sallyzoughaib that seems to be completely unrelated to this project - and honestly, it's a very weird way of installing your dependencies. Even if runtime JavaScript might pick them up, TypeScript probably doesn't.
For those for whom importing "immer" or turning "declaration" off did not work/is not an option:
If this happens for a slice: instead of exporting the slice directly, only export the specific attributes that are used to register the slice in the store:
const navigationSlicePrivate = createSlice({
name: 'navigation',
initialState,
reducers: {
/*....*/
}
}
export const navigationSlice = {
name: navigationSlicePrivate.name,
reducer: navigationSlicePrivate.reducer
}
In our application, we have around 25 slices, and only a single one of those has this problem. Comparing the slices, there seem to be no obvious difference.
The fix for me was to add declaring the slice as : Slice
in the line where createSlice
is called:
import { createSlice, Slice } from '@reduxjs/toolkit'
type MyState = {
name: string,
address: string,
}
const mySlice: Slice = createSlice({
name: 'mystate',
initialState,
reducers: {
...
},
}
@shailesh17 this isn't really a solution as you lose all specific information about the slice - what actions it has, its name, etc
this problem is described here https://typescript.tv/errors/#ts2742
Hey the solution is to:
- add
immer
to devDependencies- add
import 'immer'
on top of your file, this will not import anything at runtime but it will import the types to make the compiler happyWe could fix this inside
redux-library
by enforcing the type ofslice.actions
to not includeWritableDraft
as part of it (it's an implementation detail either way)
We see this issue a lot using rtk in our monorepo and solve it in the way noted here, i.e., an import of immer as a dev dependency. However, I do think the error is actually a typing error (albeit a minor one) namely that the return type of createSlice
is dependent on the type of WriteableDraft
from immer and yet that type is not exposed by rtk. One way to conceptualize the problem is to think about what would happen if you had a library that relied on rtk and, for some reason, your library wanted to export for its consumers the return value of createSlice
. tsc
wouldn't be able to create the declaration file for that export because it can't "describe" the WriteableDraft
type based solely on the type exports of rtk. I believe the reason this doesn't show up in many installations is because many folks are using rtk at the "top" level of their app and so the compiler never needs to worry about generating rtk-dependent types--rtk's typing is solely used to compile code, not generate declarations. but in a monorepo structure it's much easier to end up with internal libraries that have exports (and therefore generate declaration files) that depend on rtk's types, and so these sorts of incomplete type-dependency tree issues get exposed.
It would seem to me the "correct" solution would be for rtk to either re-export the WriteableDraft
type so the compiler can "see" it when constructing the createSlice
return type (and any dependent declarations) or alter the typing of createSlice so its return type converts WriteableDraft<T>
to just T wherever it occurs. I would think that would be fine as immer is only actually used inside the function.
If it was my library, I'd probably opt for the latter solution. (And this is, in effect, what the solutions here are doing that suggest typing state
in the case reducers as--they are effectively replacing WriteableDraft<State>
with State
.)
But, in any event, do @markerikson or @phryneas have a view on whether they'd be willing to implement either of these solutions? Or an explanation of why i'm off the mark on that this is a problem (however small)?
In fairness the import immer solution as a dev dependency isn't terrible, and as heavy monorepo users we are used to seeing this kind of implicit type dependency when we use third party libs, but rtk is so well typed in general, it always bugs me that it has this little bit of type pollution. đŸ˜ƒ
we don't export the WriteableDraft type because we export the Draft type.
It also wouldn't be correct for the case reducers to change Draft
we don't export the WriteableDraft type because we export the Draft type.
It also wouldn't be correct for the case reducers to change Draft to T - the point is that Draft makes a readonly object mutable, and by changing that you would be able to unsafely pass a readonly state to a mutating case reducer.
Thanks! Finally found the alternative to WritableDraft after migrating to RTK 2
None of the solutions mentioned above worked for me. I ended up doing this as work around. Yes there is an any
type in there. I don't care, it's close enough.
import {
AsyncThunk,
createAsyncThunk,
} from '@reduxjs/toolkit'
export const fetchPosts: AsyncThunk<Array<Post>, void, any> = createAsyncThunk<Array<Post>>(
'posts/fetchPosts',
async () => {
const response = await client.get('/api/posts')
return response.data
}
)
Oh and by the way, I ran into this issue while working through the Redux essentials tutorial however I was doing the tutorial in TS instead of JS.
@Dan503 on a related note, I have an almost-complete rewrite of the "Essentials" tutorial that uses TS here:
I'd appreciate any feedback you have on that (over in the PR), especially given that you've just gone through the tutorial.
(Also, fwiw, I didn't run into that error myself while updating the example app.)
None of the above worked for me either. Here's an alternative:
tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@reduxjs/toolkit": ["node_modules/@reduxjs/toolkit/"] },
// ...
}
}
@Tenekedzhiev @Dan503
Can you run this command and try again to see if it fixes the issue:
npm install https://pkg.csb.dev/reduxjs/redux-toolkit/commit/06a30ee4/@reduxjs/toolkit
I had the same issue but when I run this command npm install https://pkg.csb.dev/reduxjs/redux-toolkit/commit/06a30ee4/@reduxjs/toolkit
by @aryaemami59 the issue got fixed
I had this issue and to solve it I just had to add a type to the reducer I want to export. In the presented case, it should be: export const POSSlice : any = createSlice({ name: 'pos', initialState, reducers: { setAddedPOSItems: (state, action: PayloadAction<POSItem[]>) => { state.addedPOSItems = action.payload; } } });
At least to me that was enough. Hope it helps y'all!
@TomasDannunzioFIT
I had this issue and to solve it I just had to add a type to the reducer I want to export. In the presented case, it should be:
export const POSSlice : any = createSlice({ ... })
Please don't do that. You have destroyed all of the types information from the createSlice
function.
You are no longer getting any benefits from TypeScript by typing POSSlice
as any
.
@TomasDannunzioFIT : no, you absolutely must not do that!!!!. As said, that will remove all TS types and destroy any usefulness.
This error shows up in VS Code when using TypeScript. The slice code:
The store config
Versions:
Full error message:
Reinstalling node_modules or clearing the cache didn't help. Do you need any more information?