Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The inferred type of 'X' cannot be named without a reference to '@reduxjs/toolkit/node_modules/immer/dist/internal'. This is likely not portable. A type annotation is necessary. #1806

Closed
filiptrplan opened this issue Dec 4, 2021 · 41 comments

Comments

@filiptrplan
Copy link

filiptrplan commented Dec 4, 2021

This error shows up in VS Code when using TypeScript.
The slice code:

import { POSItem } from './../POSService';
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '../../../app/store';

export interface POSState {
    addedPOSItems: POSItem[],
}


const initialState = {
    addedPOSItems: []
} as POSState;

export const POSSlice = createSlice({
    name: 'pos',
    initialState,
    reducers: {
        setAddedPOSItems: (state, action: PayloadAction<POSItem[]>) => {
            state.addedPOSItems = action.payload;
        }
    }
});

export const { setAddedPOSItems } = POSSlice.actions;
export const selectAddedPOSItems = (state: RootState) => state.pos.addedPOSItems;
export default POSSlice.reducer;

The store config

import { configureStore } from '@reduxjs/toolkit';
import POSSlice from '../client/POS/POSService/POSSlice';

const store = configureStore({
    reducer: {
        pos: POSSlice
    },
});

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export default store;

Versions:

    "react": "^17.0.2",
    "redux": "^4.1.0",
    "@reduxjs/toolkit": "^1.6.1",

Full error message:

"The inferred type of 'POSSlice' cannot be named without a reference to '@reduxjs/toolkit/node_modules/immer/dist/internal'. This is likely not portable. A type annotation is necessary."

Reinstalling node_modules or clearing the cache didn't help. Do you need any more information?

@markerikson
Copy link
Collaborator

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.

@filiptrplan
Copy link
Author

I'm quite inexperienced in TypeScript. Can you maybe just point me in a direction where I should start troubleshooting?

@filiptrplan
Copy link
Author

filiptrplan commented Dec 5, 2021

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,
  },
}

@james-mungai
Copy link

@filiptrplan thanks

@bfricka
Copy link
Contributor

bfricka commented Jan 29, 2022

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:

  • Same tsconfig (nearly, see below)
  • Same RTK version
  • Same TypeScript version
  • The one without the error was importing castDraft from immer, so I tried that, thinking the import might trigger some bail out logic. Didn't do squat.
  • Both packages I tested on within the separate monorepos have RTK peer dependency defined as ^1.7
  • The various apps within these projects have RTK as a dependency (shouldn't matter, because it's not the apps but the supporting packages that I'm building and publishing using tsc).
  • None of the packages within either monorepo have 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.

@bfricka
Copy link
Contributor

bfricka commented Feb 25, 2022

@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.

@NoahAndrews
Copy link

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.

@pliao1996
Copy link

export const templateSlice: any = createSlice({ name: 'template', initialState, *****

only need to declare the slice explicitly to resolve this ts type error

@markerikson
Copy link
Collaborator

@PeiLiao : please don't do that :( You're throwing away all the actual TS types in templateSlice.

@NickBolles
Copy link

Since google brought me to this one first, linking to my comment confirming a fix in the referenced issue: #1181 (comment)

@JCB-K
Copy link

JCB-K commented Jul 19, 2022

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.

@isc30
Copy link

isc30 commented Nov 22, 2022

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 happy

We 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)

@taylorkline
Copy link
Contributor

Adding immer to devDependencies is not strictly necessary.

A simple import "immer" at the top of the file quiets the error.

@mmncit
Copy link

mmncit commented Jan 18, 2023

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 happy

We 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)

In my case, unfortunately, none of these setups is working.

Attempts:

  1. not adding immer to devDependencies and import 'immer' on top of slice.ts file
  2. adding immer to devDependencies and import 'immer' on top of slice.ts file

The 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?

@ly3xqhl8g9
Copy link

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 happy

We 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)

In my case, unfortunately, none of these setups is working.

Attempts:

  1. not adding immer to devDependencies and import 'immer' on top of slice.ts file
  2. adding immer to devDependencies and import 'immer' on top of slice.ts file

The 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?

A solution might be adding "preserveSymlinks": true in the "compilerOptions" of tsconfig.json.

@srosato
Copy link

srosato commented May 5, 2023

// 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.

@nouman91
Copy link

Thank you @srosato this worked!

@carneiror
Copy link

carneiror commented Aug 1, 2023

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.

@vdawg-git
Copy link

vdawg-git commented Sep 7, 2023

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.

@srosato
Copy link

srosato commented Sep 11, 2023

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.

I also recently switched to pnpm and then I did the same as you.

What I did:

  1. Get version of immer used by redux-toolkit cat node_modules/@reduxjs/toolkit/package.json | grep immer
  2. pnpm i immer@^9.0.21 // in my case redux-toolkit reported using ^9.0.21

Then 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.

@srosato
Copy link

srosato commented Sep 11, 2023

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 #1806 (comment) worked on ^9.0.7 for me

@SharmaTushar
Copy link

Importing immer didn't work for me, so I referenced it as

/// <reference path="../node_modules/immer/dist/immer.d.ts" />

@zkWildfire
Copy link

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;
        }
    },
});

@haozi
Copy link

haozi commented Nov 23, 2023

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" />

@phryneas
Copy link
Member

phryneas commented Dec 7, 2023

@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.

@nuarhu
Copy link

nuarhu commented Jan 17, 2024

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.

@shailesh17
Copy link

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: {
      ...
    },
}

@EskiMojo14
Copy link
Collaborator

@shailesh17 this isn't really a solution as you lose all specific information about the slice - what actions it has, its name, etc

@skurgansky-sugarcrm
Copy link

this problem is described here https://typescript.tv/errors/#ts2742

@sam-mfb
Copy link

sam-mfb commented Feb 3, 2024

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 happy

We 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)

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. 😃

@EskiMojo14
Copy link
Collaborator

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.
image

@boan-anbo
Copy link

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. image

Thanks! Finally found the alternative to WritableDraft after migrating to RTK 2

@Dan503
Copy link

Dan503 commented Jun 28, 2024

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.

@markerikson
Copy link
Collaborator

@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.)

@Tenekedzhiev
Copy link

None of the above worked for me either. Here's an alternative:

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@reduxjs/toolkit": ["node_modules/@reduxjs/toolkit/"] },
   // ...
  }
}

@aryaemami59
Copy link
Contributor

aryaemami59 commented Jul 10, 2024

@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

@Born2shine
Copy link

Born2shine commented Jul 10, 2024

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

@TomasDannunzioFIT
Copy link

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!

@Dan503
Copy link

Dan503 commented Jul 16, 2024

@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.

@markerikson
Copy link
Collaborator

@TomasDannunzioFIT : no, you absolutely must not do that!!!!. As said, that will remove all TS types and destroy any usefulness.

@reduxjs reduxjs locked as resolved and limited conversation to collaborators Jul 16, 2024
@markerikson
Copy link
Collaborator

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests