TypeScript: The inferred type of "X" cannot be named without a reference to "Y". This is likely not portable. A type annotation is necessary.

Bug Report

🔎 Search Terms

inferred type cannot be named, symlink node_modules

🕗 Version & Regression Information

I’m verifying the problem on the typescript@4.1.3. I’ve not tried older versions but at least is also reproducible on the @next version as of today.

It is probably a regression or a corner case related with other issues opened and already closed like:

⏯ Playground Link

Link for a repo where the problem is being reproduced

NOTE: Just clone the repo and run yarn tsc

💻 Code

All the relevant code can be found at https://github.com/mistic/reproduce-typescript-problem-when-symlinking-node_modules

It is just reproducing a similar setup that I had on other project that was generating the problem:

  • node_modules are a symlink to another location that is not a direct parent of the symlinked node_modules
  • we are using types in the compilation from a library where those types are just exported from other one, like for example withRouter within react-router-dom that is just a plain export from the same type on react-router.

🙁 Actual behavior

I got the following error:

error TS2742: The inferred type of 'Nav' cannot be named without a reference to '../../deps/node_modules/@types/react-router'. This is likely not portable. A type annotation is necessary.

8 export const Nav = withRouter(({ history }: NavProps) => {
               ~~~


Found 1 error.

🙂 Expected behavior

I was expecting no error at all and that the typescript compiler was just able to find all the respective modules. I’ve tried everything that I thought was related like enabled the preserveSymlinks. The only thing that stops the error is importing the withRouter type directly from react-router and not from react-router-dom but that doesn’t make very sense because I actually want to use the react-router-dom on a couple of places.

\cc @weswigham @sheetalkamat @andrewbranch because I saw you previously worked to try to solve similar issues.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 170
  • Comments: 136 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Im using a pnpm monorepo and had a similiar problem which I could resolve by setting baseUrl: "." inside the package tsconfig.json

I get this issue throughout many scenarios when using a Rush monorepo. Rush, by default, uses pnpm (pnpm workspaces is recommended). I haven’t been able to track down the root cause, but I have scoured this issue tracker and subscribed to many topics hoping someone squashes this.

In my case, add preserveSymlinks for resolve it

{
  "compilerOptions": {
    "preserveSymlinks": true
  }
}

For me, the "preserveSymlinks": true in tsconfig.json helped.

In my case, add preserveSymlinks for resolve it

{
  "compilerOptions": {
    "preserveSymlinks": true
  }
}

This worked for me.

Note: I had to restart my TypeScript Server for this to have an effect on my IDE

I managed to fix this by adding "declaration": false to compilerOptions in tsconfig.json for my package, adding it here in case it helps someone else.

most of you (including me🫠) doesn’t read the error message. A type annotation is necessary: explicit type annotation solves the problem.

so

export const useMongoRealtimeProvider=() => {
...
}

becomes

export const useMongoRealtimeProvider: () => Socket = () => {
...
}

pnpm + turbo + typescript -> trying to avoid hoisting. typescript errors out with tsup in the bundling phase.

❌ workaround - import type {} from ‘Y’
❌ workaround - declaration option in tsconfig’
❌ workaround - baseUrl and linking to packages

i have a massive project with crazy amount of packages… it is very hard for me to manually work around this issue.

if anyone got more workarounds, i’d be happy to test them out. i am the last attempt before hoisting everything and waiting for an official fix.

At the very least, it would be useful to have a setting in tsconfig.json for the TS compiler to make pkg B fail compilation unless const obj = obj_() has a type annotation.

There is one. declaration: true.

Also facing this issue in a pnpm + turborepo + typescript project. The fun fact is, if i manually give it the type it works. What i mean is:

import { myObj } from 'package'

const newObj = myObj     // ---> error
import { myObj } from 'package'

const newObj: typeof myObj = myObj   // ---> everything works

This is definitely a bug as this cannot possibly be an expected behaviour.

EDIT: i tried the preserveSymlinks trick but didn’t work for me.

This is driving me absolutely bonkers in a pnpm monorepo. Seems similar to https://github.com/microsoft/TypeScript/issues/47663

FYI I added a comment in TypeScript#47663 which has workarounds for this issue when using pnpm monorepos.

Hm, it’s a bit awkward in your case, since we technically never directly discover that react-router is directly visible from your project folder (our symlink discovery is a bit heuristic based, so we only find them in paths we had to resolve a symlink across to access). If you had a react-router import somewhere in the file already, we’d know we have a lookup available… Hm. @RyanCavanaugh today we don’t use a package.json for anything within a TS project, however with the recent changes to node resolution, we really should start considering it (so we can resolve package self names and "imports" maps) - as part of that, we could maybe consider using the dependencies manifest as a list of safe-to-access modules, like how we already use it for autoimports in the language server.

I had the same error with Prisma. Wrong code

import { PrismaClient } from '@sportsbase-libs/database-mapping-next';
import { databaseUrl } from './databaseUrl';

export const mapping = new PrismaClient({
  datasources: {
    db: {
      url: databaseUrl('mapping'),
    },
  },
  log: process.env.DEBUG ? ['query', 'info', 'warn', 'error'] : [],
});

Fixed code

import { PrismaClient } from '@sportsbase-libs/database-mapping-next';
import { databaseUrl } from './databaseUrl';

export const mapping: PrismaClient = new PrismaClient({
  datasources: {
    db: {
      url: databaseUrl('mapping'),
    },
  },
  log: process.env.DEBUG ? ['query', 'info', 'warn', 'error'] : [],
});

I faced this problem using turborepo + typescript + stitches

Error:

web:build: ../library/ui/src/theme/stitches.config.tsx:3:16
web:build: Type error: The inferred type of 'styled' cannot be named without a reference to '../../../../web/node_modules/@types/react'. This is likely not portable. A type annotation is necessary.
web:build:
web:build:   1 | import { createStitches } from '@stitches/react';
web:build:   2 |
web:build: > 3 | export const { styled, getCssText } = createStitches({
web:build:     |                ^
web:build:   4 |   theme: {
web:build:   5 |     fonts: {
web:build:   6 |       system: 'system-ui',

Solution:

import { createStitches } from '@stitches/react';

export const { styled, getCssText } = createStitches({
     ...
}) as any;  <-- here - Type any

In package it was installed as devDependency, and in main app as dependency

When is this going to get fixed? Why should anyone disable the declaration option when it’s needed? Can Microsoft stop f***ing around and keep working on projects it has started?

node-linker=hoisted

is the only thing that fixed in pnpm.

I tried v8 as well, but no luck.

Something to note:

I had to do:

find . -name 'node_modules' -type d -prune -print -exec rm -rf '{}' \;
pnpm i

to make it work.

Simply doing pnpm i && pnpm dedupe was still throwing an error.

same problem here with pnpm (symlinks/hardlinks)

tsconfig.json
declaration flag
tsconfig.json
strict flag
does it work?
true true NO
false true YES
true false YES

Disabling declaration and declaration map in the tsconfig for the application importing the package in my pnpm monrepo worked for me.

A reminder for myself: this is the solution!

"declaration": false,
"declarationMap": false,

I got the problem of inferring type of t in

const t = initTRPC.context<Context>().create();

Setting "moduleResolution" to "node" instead of "node16" in tsconfig.json worked for me.

In case you’re using pnpm as package manager in a monorepo you can get this error because of the default behavior of pnpm to symlink the dependancies when we do pnpm install. TypeScript act weird when symlinks are used in node_modules for some reason.

Temporary Fix is to tell pnpm to create flat node_modules without symlinks.

Create a .npmrc file in the root folder of your project & add the following.

auto-install-peers = true
node-linker = hoisted

Run pnpm install

Read more about node-linker

**I was using turborepo with pnpm as the package manager.

I removed "declaration": true , "preserveSymlinks": true" and restart ts server

Reading this long thread, I understand the root cause is somehow related to symbolic links.

I just don’t get why TS compiler should care. Isn’t it simply of operating system behavior ? Shouldn’t TS compiler just “blindly” read the file, whether it’s linked or not ?

With #58176 merged, all of the instances of this issue that are actually us not looking up a dependency you actually have available (because of symlinks preventing us from realizing some path equivalences) should be fixed in the next release, barring any funky unforeseen bugs (which, if you think you can demonstrate, please open a new issue for us to consider). Which is what the OP’s issue was long, long ago. Other people in this thread… much less so.

That means that so long as you actually have the dependencies your declaration file needs as direct dependencies (in your package.json), we’ll be able to autogenerate appropriate dependency names, and no longer emit this error. If you still see this error in TS 5.5 and beyond and come to this thread - I’m sorry, SEO has led you astray. This github issue having floated to the top, above even stackoverflow, for that error message is somewhat unfortunate, but not really in our control.

For the benefit of those searchers, our FAQ will likely be updated to have a detailed explanation for this error by the time you find this, but in short: You really do just need to do what the error says, and write an explicit type annotation on the node the error is marked on. You have an implicit, direct, type-level dependency on an indirect dependency’s types, and we can’t resolve that issue for you - it’s up to you how you want to reach in and get at the innards of your dependencies. Maybe you add whatever the type is coming from as a direct dependency, maybe you write your own equivalent type, maybe there’s a winding road through aliases and reexports and conditional infers to get at the type that we can’t automatically discern, but you can write as the annotation - whatever works for you.

Honestly, I’m not switching to NPM. A trash tool forces you to switch to another trash tool, lol. I don’t have free 300mb for every goddamn project to install the same package over and over again!

That sounds like it may not be hard to fix after all! Can’t the TypeScript team ask for help by sending a message on social media platforms? This will help find someone to fix this issue. If they can’t even do this, it’s better if they archive the project and let it die. This way other projects will fill the gap, hopefully! They can even try to financially support other open-source projects because clearly, they can’t maintain their projects.

the hoisting solution - doesn’t it effect the advantages of pnpm ? isn’t there another way to work with symlinks ?

Disabling emit of declaration files is only a solution if you don’t need them.

Setting "declaration": false and "declarationMap": false, in my tsconfig.json solved this for me, though this is an odd fix.

Disabling declaration and declaration map in the tsconfig for the application importing the package in my pnpm monrepo worked for me.

TLDR; I had a version mismatch of a dependency. All related packages in the monorepo needed to have the same version installed.

I don’t see it posted in here yet so this is what solved it for me: https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1270716220

Add import type {} from "Y"; where Y is the package that TS is saying you need a reference to.

@Nikola-Milovic my solution is disable the composite in tsconfig.json

{
  "compilerOptions": {
   // ...
    "composite": false,
  }
}

In monorepo, set all typescript to the same version:

"typescript": ">=4.6.3",

Then create npmrc and put some information:

legacy-peer-deps=true
auto-install-peers=true
node-linker=hoisted

Everything will run smoothly.

In my case, add preserveSymlinks for resolve it

{
  "compilerOptions": {
    "preserveSymlinks": true
  }
}

This solved it for me too, but why ? 🤔

I switched to NPM and the issue still exists!!! (same with PNPM and Bun). I’m using Windows 11. The people that were saying this only happens when using PNPM and Bun, this they test it? Am I doing something wrong? About symlinks, I don’t understand what they achieve here, no matter which package manager I use, the size of node_modules is (almost) the same!!!

Consider the following file structure:

/monorepo
  /node_modules
    /.pnpm
      /kysely@0.22.0/node_modules/kysely
  /packages
    /foo
      /src
        /index.ts
      /node_modules
        /kysely -> ../../../.pnpm/kysely@0.22.0/node_modules/kysely 

When using import k from "kysely" in index.ts, the resolved path (when hovering "kysely" in vscode) shows the absolute path on disk (/monorepo/node_modules/.pnpm/kysely@0.22.0/node_modules/kysely/...)

Since Typescript “sees” that the dependency outside foo, it display the error.

However, since the full path to the imported files was resolved by following a symlink which does reside inside foo, Typescript should not consider that this package is “not portable”.

@weswigham, it might not be needed to support package.json for this, only to “remember” that the file was resolved form a path that is reachable within the module root dir (even though through a symlink).

The inferred type of 'authSlice' cannot be named without a reference to '.pnpm/immer@9.0.14/node_modules/immer/dist/internal'. This is likely not portable. A type annotation is necessary.

This is what I get. I’m using pnpm, t ypescript and monorepo. To be precise, I’m using this: https://github.com/NiGhTTraX/ts-monorepo

authSlice is actually createSlice, and createSlice is imported from @reduxjs/toolkit. I have read the entire thread and setting the baseUrl = "." didnt work.

EDIT: I also added

"paths": {
            ".pnpm/immer@9.0.14/*": ["node_modules/.pnpm/immer@9.0.14/*"]
        },

but that also didn’t work.

EDIT 2: I managed to fix the error by deleting

    "declaration": true,
    "declarationMap": true,

from root tsconfig.build.json

I think I have a relatively painless solution which may work for some…

So in a case where we have a setup like

  • A is the original library with the unresolvable type
  • B is a helper library which wraps and exposes functionality in a library A
  • C is the end user code, which uses library B instead of interacting with A directly (and I’d like to not even declare the dependency on A)

It seems I’m able to fix the issue by re-exporting the missing type from A in B, even though C will still not import it or use it directly.

so my library in B will include something like

export type { MissingType } from 'A'

and C can continue to interact with B as it was before…

Would be great if this is fixed but this will certainly work for now

This is workaround 3.1 of my “workarounds comment” buried deep in this discussion: https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189

@mistic may I ask you to update the issue to include a link to these workarounds? The many upvotes indicate it is/was helpful for many people, and I would like to stop commenting here spamming all issue subscribers just to “resurface” the workarounds 😃

For the ones joining the issue recently:

The root cause for this issue is described by this comment: https://github.com/microsoft/TypeScript/issues/42873#issuecomment-1941449175

Main issue is due to people using PnPM or Bun, which are installing packages as symlinks instead of copying files. Then Typescript follows the symlink and finds out the referenced file is out of the project root, which triggers the error.

Not sure on how to fix this. But the most important is to know the root cause.

I’m getting this error when declaring handlers array for MSW, having "moduleResolution": "bundler" in tsconfig. Using "moduleResolution": "Node" makes the error go away.

I also faced this problem. I think the point is that the typescript, due to a bug, cannot correctly resolve complex types or types scattered over many directories. To get rid of the error, you need to manually infer the type of the problematic element through the as keyword. And the error goes away. Like this:

const AbsoluteLayout = forwardRef<AbsoluteLayoutProps, AbsoluteLayoutRef>(
  createComponent((props, ref) => absoluteLayout({ ref, ...props })),
) as Component<AbsoluteLayoutProps, AbsoluteLayoutRef>; //<---- Manual output type

Im using pnpm and had a similiar problem too and I resolve this by setting “shamefully-hoist=true” in “.npmrc”

TypeScript 💪, LOL

import * as _1 from "../../../node_modules/.pnpm/yaml@2.4.1/node_modules/yaml/dist/index.js"
import * as _2 from "../../../node_modules/remark-gfm/lib/index.js"
import * as _3 from "../../../node_modules/.pnpm/mdast-util-toc@7.0.0/node_modules/mdast-util-toc/lib/index.js"
import * as _4 from "../../../node_modules/rehype-slug/lib/index.js"
import * as _5 from "../../../node_modules/rehype-autolink-headings/lib/index.js"
import * as _6 from "../../../node_modules/rehype-external-links/lib/index.js"

When is the TypeScript team going to fix this 4-year-old issue?! @weswigham

@typescript-bot @RyanCavanaugh @jakebailey

Can you get this fixed, please?

While I don’t agree with the wording here https://github.com/microsoft/TypeScript/issues/42873#issuecomment-1998466411 I do agree with the sentiment. It’d be nice if a majority of these “quirks” were ironed out.

I’ve been running into this problem non-stop with project references in a monorepo. We had to move the tsconfig.json file to the root of the monorepo and stop using project references completely, and things are working relatively well but now the issue is we can’t divide compilation per project and the entire project has to be compiled every single time.

Because it is hidden in the >100 comments, I want to surface my workarounds list again https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189. @transitive-bullshit this should also explain to you why setting these two options to false helped in your case.

Just wanted to add to the set of possible solutions: this error can occur when working in a monorepo. Essentially, what happens is that you have two packages, A and B. B consumes components from A. If you use some types in A that also exist in B, you need to be sure that the versions of such types are consistent with each other. If not, the compiler in the consuming package will alert you to a type it doesn’t have access to, or in the case of NextJS, even fail to pnpm build at the type linting stage.

As a concrete example:

If you have a monorepo with a UI package written with react, your types may include @types/react ^18.0.0 . If the consuming package is using @types/react <18, the types may not agree, causing the issue at build time. In nextJS particularly, dev works just fine because the compiler doesn’t do strict linting as you work. However, at build time, it will throw the error.

You can fix this by doing two things:

  1. ensure that the consuming package uses the minimum supported types that are implied by the source package, preferably within the same major type version (to prevent breaking changes, presuming a semver-compliant package)
  2. rather than marking types that will exist in the consuming package as dev dependencies, mark them as peer dependencies inside your package json. This should tell your consuming compiler that it’s not actually supposed to use the dependencies that were specified in the first package, but rather use that as a guide to ensure that it has the minimum supported types

I got the type annotation error that makes up the subject of this thread because my consuming package was using v17 types (@types/node, @types/react/react, @types/react-node). Just going into my package.json, setting them to the latest major version, and running pnpm update fixed my problem. Granted, it took me 3 days to even understand what I was doing wrong lol.

Hope this helps someone.

"preserveSymlinks": true doesn’t resolve all errors that I have. I also use pnpm and typescript together. Previously I used yarn and typescript and everything was fine.

Now, I have The inferred type of ‘someVar’ cannot be named without a reference to '@pnpm-test/core-config/node_modules/yup/lib/string'. This is likely not portable. A type annotation is necessary. and have no idea how to resolve that.

This appears if I set

"baseUrl": "./",
"paths": {
            "*": [
                "node_modules/*/"
            ]
        }

If I remove that, I also have the same issue but in other places.

The way to fix this exact problem described above was setting path to exact module (yup in my case), but then I still get other similar errors. That’s super frustrating.

I also set declaration: false for testing purposes but had no luck having it working.

In my case, the question looks like this:

// pkgA
import { someSDK } from 'xxxx';

export interface Handler {
  handler: () => someSDK;
}
export function getHandler(): Handler {}
// pkgB
import { getHandler } from 'pkgA';

// this line will show the error: The inferred type of 'handler' cannot be named without a reference to 'xxxx/node_modules/xxxxx'. This is likely not portable. A type annotation is necessary.
const { handler } = getHandler();

And I solve this through add the export { someSDK } in pkgA

// pkgA
import { someSDK } from 'xxxx';

export { someSDK };
export interface Handler {
  handler: () => someSDK;
}
export function getHandler(): Handler {}

I hit this error tryout out npm 9.4’s new install-strategy=linked in a monorepo using npm workspaces.

One interesting thing about this is that importing from a monorepo module that re-exports causes the error:

import {BaseComponent, html} from 'my-base-component';

export class MyElement extends BaseComponent {
  render() { return html`...`; }
  // ^ error on the return type of render()
}

but importing from the original library fixes the error:

import {BaseComponent} from 'my-base-component';
import {html} from 'lit';

export class MyElement extends BaseComponent {
  render() { return html`...`; }
  // ^ no error
}

Using "preserveSymlinks": true with pnpm is generally not safe, because then tsc will treat different symlinks to the same module as different modules altogether.

I have this error with @trpc/server in a monorepo, using yarn@3.2.1.

I tried all the solutions in this issue and none worked. 😭

I’ve made an issue on trpc repo with a repro : https://github.com/trpc/trpc/issues/1945

edit

It seems like I have to use a wildcard in the paths and it solves the issue ✅

{
  "extends": "@acme/tsconfig/node",
  "compilerOptions": {
    "baseUrl": "src",
    "outDir": "./dist",
    "rootDir": "./src",
    "paths": {
      "@trpc/server/*": ["../../../node_modules/@trpc/server/*"]
    }
  },
  "include": ["src/**/*.ts"]
}

Like @jsheely I don’t understand why it works that way and if it’s a typescript resolution issue or if it’s a misuse of typescript at the library level.

In my case: When I use declaration: true and yarn as package manager -> got same error cannot be named without a reference to 'styled-components/node_modules/@types/react After researching I found mismatch versions of @types/react for my repo and dependency styled-components/node_modules/@types/react

If run yarn install --flat, manually fill subpackage resolutions and run yarn build - problem is disappear

@InsOpDe pnpm also installs flat dependencies for monorepo with resolutions and you avoided my problem 😃

I’ve got a repro which may help with the fix available here: paynecodes/ts-repro-type-annotation-require-symlinks. I don’t want to hijack this issue if it’s the wrong place, so let me know if a new issue is desirable.

The README lists references to similar issues I was able to dig up.

Same issue here

import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

...
export const useAppStore = create<AppState & AppActions>()(
	immer(
		devtools((set) => ({
			user: null,
			setUser: (user) => set({ user }),
		})),
	),
);

The inferred type of 'useAppStore' cannot be named without a reference to '.pnpm/immer@9.0.6/node_modules/immer/dist/internal'. This is likely not portable. A type annotation is necessary.

"typescript": "5.1.6"

The tsconfig

/dashboard ➜ tsc --showConfig
{
    "compilerOptions": {
        "composite": false,
        "declaration": true,
        "declarationMap": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "inlineSources": false,
        "isolatedModules": true,
        "preserveWatchOutput": true,
        "strictNullChecks": true,
        "skipLibCheck": true,
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noFallthroughCasesInSwitch": true,
        "target": "es2020",
        "useDefineForClassFields": true,
        "lib": [
            "es2020",
            "dom",
            "dom.iterable"
        ],
        "module": "esnext",
        "moduleResolution": "bundler",
        "allowImportingTsExtensions": true,
        "noEmit": true,
        "jsx": "react-jsx",
        "baseUrl": "./",
        "paths": {
            "@dashboard/*": [
                "./src/*"
            ]
        }
    },
    "references": [
        {
            "path": "./tsconfig.node.json"
        }
    ],...
}

{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

This is the default vite tsconfig generated (with minor tweaks)

I’m getting this error when declaring handlers array for MSW, having "moduleResolution": "bundler" in tsconfig. Using "moduleResolution": "Node" makes the error go away.

Same thing here, using znv’s parseEnv with zod types. I get the warning only when my tsconfig has bundler for moduleResolution (as per Bun’s suggested compilerOptions)

I was getting this error in a pnpm monorepo. I fixed it by switching paths in my tsconfig.json to point to my package in node_modules, rather than relative to the monorepo directory structure.

Before (bad)

"paths": {
  "@org/my-package": "../my-package/src"
}

After (good)

"paths": {
  "@org/my-package": "./node_modules/@org/my-package/src"
}

I posted a solution earlier, but I run into this error somewhere around every month and I need a different solution every time. This time my solution was to explicitly define my function’s return type, rather than forcing TypeScript to infer it.

from

export function doThing() {
    const stuff = doSomething();
    return {
        stuff,
    };
}

to

import {ExplicitlyImportedType} from 'whatever'

export function doThing(): {stuff: ExplicitlyImportedType} {
    const stuff = doSomething();
    return {
        stuff,
    };
}

@enyelsequeira

In reply to https://github.com/i18next/next-i18next/issues/2115

If you’re on pnpm >= 7.29.0 || 8.0.0-alpha you might try the following settings in your .npmrc. Most of them will be default in upcoming v8.

Nice to read: https://github.com/pnpm/pnpm/releases/tag/v7.29.0 / dedupe-peer-dependents

# Not always possible to be strict, but if it works for you, keep it to true.
# https://pnpm.io/next/npmrc#strict-peer-dependencies

strict-peer-dependencies=false
auto-install-peers=false

# Will default in v8
# https://pnpm.io/next/npmrc#use-lockfile-v6
use-lockfile-v6=true


# From 7.29.0, default in v8
# https://github.com/pnpm/pnpm/releases/tag/v7.29.0

dedupe-peer-dependents=true

# Helps with peer-deps (>=7.23.0), default in v8
# https://pnpm.io/npmrc#resolve-peers-from-workspace-root

resolve-peers-from-workspace-root=true

Then run

pnpm install && pnpm dedupe

Theoretically it should work.

I am also wondering are there any drawbacks

By using node-linker = hoisted, pnpm will store your deps +/- like npm does (more compatible). Thus loosing some pnpm features.

There is a related issue (#29808) as implicitly pointed out by @nwaughachukwuma in his comment. I solved it after following @akash-rajput's answer within the same thread.

To quote:

Happens when we have conflicting versions of a package installed. A temporary solution would be to remove the package mentioned from peer dependencies of the plugin giving the error.

Or install the exact version of common dependency

In my case, I had linked a local NPM package with peer dependency @nestjs/common@^9.0.7 to a monorepo that had @nestjs/common@^8.0.0 installed.

I’m using typescript@4.7.2.

Has anyone found any other solutions? I use Turborepo with pnpm, there are two identical Nestjs apps works on different ports, but only one causes an error: src/app.controller.ts:18:2 - error TS2742: The inferred type of 'getHello' cannot be named without a reference to '.pnpm/rxjs@7.8.1/node_modules/rxjs'. This is likely not portable. A type annotation is necessary.

Suddenly had this problem with Nuxt 3.8.0 in the nuxt.config.ts.

Not sure why, because I haven’t changed the tsconfig.base.json.

However, setting "declaration": false worked for me

As a workaround you can enforce the module path by aliasing in tsconfig.json.

From the example ../../deps/node_modules/@types/react-router would turn into: (assuming the module is also installed in the app and present in node_modules)

{
  "extends": "*",
  "compilerOptions": {
    ...
    "outDir": "./dist",
    "baseUrl": "./",
    "paths": {
        "~/*": ["src/*"],
        "@types/react-router": ["node_modules/@types/react-router"],
     },
  },
  "exclude": ["node_modules", "dist"],
  "include": ["src", "test"]
}

It’s basically enforcing peerDependency-like setup.

the hoisting solution - doesn’t it effect the advantages of pnpm ? isn’t there another way to work with symlinks ?

In case you’re using pnpm as package manager in a monorepo you can get this error because of the default behavior of pnpm to symlink the dependancies when we do pnpm install. TypeScript act weird when symlinks are used in node_modules for some reason.

Temporary Fix is to tell pnpm to create flat node_modules without symlinks.

Create a .npmrc file in the root folder of your project & add the following.

auto-install-peers = true
node-linker = hoisted

Run pnpm install

Read more about node-linker

**I was using turborepo with pnpm as the package manager.

this did the trick for me, but I am also wondering are there any drawbacks?

I’ve faced today this error in nx monorepo with pnpm, while playing and experimenting setting vite-plugin-node as node server, and started overuse different compiler options in the root tsconfig.base.json file. I’ve realized that setting composite key to true, started giving me that error, or in my case more specific:

The inferred type of 'apiGateway' cannot be named without a reference to '.pnpm/@types+express-serve-static-core@4.17.31/node_modules/@types/express-serve-static-core'. This is likely not portable. A type annotation is necessary.

@Dealerpriest not sure if u already tried the composite key set to false or removing it.

Removing composite key or value to false works for me

I had the same problem while using pnpm. The error occurred due pnpm installing multiple copies of the same npm package. Here is what worked for me:

  1. Remove the offending npm package from each package and moving it into dependencyPeer list.
  2. Add the package only for the app

Then pnpm did not install multiple versions of the same npm package and that finally fixed this annoying typescript error

@mistic try this: add below code into this file: https://github.com/mistic/reproduce-typescript-problem-when-symlinking-node_modules/blob/main/project/tsconfig.json

{
 "paths": {
   "@types/react-router": ["../../deps/node_modules"]
 },
}

it works!

My understanding of declaration: true is that a *.d.ts file is created. It would be great to navigate directly to the *.ts source.

To support both *.ts & *.d.ts files in an npm package, something like "declarationDir": "dist" is necessary, otherwise, *.ts* files will be imported due to import precedence.

Another minor issue that I have to account for is with single file components which are compiled separate from Typescript. For example a svelte or vue component. These components will need to be copied over to the dist directory during npm run build, since npm does not support links within packages.

These are both minor but annoying issues. Particularly annoying when working with monorepos/multirepos with many packages. It’s rare & tooling can fix the build issues. It seems like *.d.ts is useful in having a standard to ensure that type inference works in all cases, but it unfortunately breaks navigation. Of course the tools (VSCode, Jetbrains) would need to address the navigation problem as it stands.

Would it make sense to annotate the path to the source of the *.d.ts file in a comment, like a *.map file?

@weswigham Thanks for looking into the issue. What you wrote makes sense in my head as an explanation about why it is this bug happening. Let me know if I can be useful in any other way to move forward with this one as I really hope we can fix it!

I’m using prisma. Importing type from prisma client seem to fix this error(Even though I’m not using this type)

import { Product } from '@prisma/client';

@Injectable()
export class ProductsService {
  findAll() {
    return this.prisma.product.findMany();
  }
}

@rhuanbarreto

Main issue is due to people using PnPM or Bun, which are installing packages as symlinks instead of copying files. Then Typescript follows the symlink and finds out the referenced file is out of the project root, which triggers the error.

AFAIK bun uses hardlinks:

Since Bun uses hardlinks to “copy” a module into a project’s node_modules directory on Linux, the contents of the package only exist in a single location on disk, greatly reducing the amount of disk space dedicated to node_modules.

https://bun.sh/docs/install/cache#saving-disk-space

Importing the packages in the entry file of the library or project solved my problem

same error occurs with lodash-es too. It is really a nightmare to use typescript with nodejs. you set up your development environment but transpiled code not working for a stupid difference in module resolution. tsc not supporting path aliases, you cannot use esm packages with commonjs so you have to use modules but damn than nodejs gets crazy and you try other tools for a simple build process!

I have a pnpm monorepo with a package and an app using that package:

pnpm-monorepo
  apps
    react-frontend (uses "backend-adapters". has a dependency on the "msw" package)
  packages
    backend-adapters (has a dependency on the "msw" package)
      adapters
      schemas
      mocks

The type error that prevents building looks like this in the backend-adapters package:

The inferred type of setupRestAPIWorker cannot be named without a reference to ../../node_modules/msw/lib/glossary-de6278a9 . This is likely not portable. A type annotation is necessary.

where setupRestAPIWorker is just:

import { restHandlers } from "../mocks/handlers";
import { setupWorker } from "msw";

/**
 * Mock Service worker for all REST API adapters in the domain package
 * @returns a MSW instance
 */
const setupRestAPIWorker = () =>
  setupWorker(...Object.values(restHandlers).flat()); // red squiggly line in VSCode shows up in the const here thanks to Typescript

export { setupRestAPIWorker };

The Typescript configuration for this package comes from the root of the monorepo. There is a tsconfig.base.json in the root of the monorepo that is shared by all the apps and packages:

{
  "compilerOptions": {
    "moduleResolution": "node16",
    "target": "ES2022",
    "module": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "noImplicitReturns": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}

There’s also a tsconfig.lib.json in the root of the monorepo intended for use by packages specifically:

{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "noEmitOnError": true,
    "emitDeclarationOnly": true
  }
}

So our package backend-adapters uses the lib config above like so:

{
  "extends": "../../tsconfig.lib.json",
  "compilerOptions": {
    "strict": true,
    "sourceMap": true,
    "declarationDir": "lib/types",
    "resolveJsonModule": true,
    "noEmitOnError": false,
    "outDir": "lib",
    "isolatedModules": true,
    "useDefineForClassFields": true
  },
  "include": ["src/**/*", "package.json"]
}

Based on how config inheritance works, this means that moduleResolution for this package is set to Node16.

So at this point, I’ve tried all the solutions presented in @pkerschbaum 's comment way above and I found that none of them worked. What actually worked was changing the moduleResolution in the package tsconfig.json back to Node. This option is clearly deprecated so this doesn’t feel ideal. 😢

Tooling versions:

  • Typescript 4.9.5
  • MSW 1.3.2
  • Node 18.15.0

Edit 1:

Further debugging: I ran tsc --traceResolution in the package with moduleResolution: Node16 and got this

======== Module name 'msw' was successfully resolved to '/[obfuscated-path]/pnpm-monorepo/node_modules/.pnpm/msw@1.3.2_typescript@4.9.5/node_modules/msw/lib/index.d.ts' with Package ID 'msw/lib/index.d.ts@1.3.2'. ========
File '/[obfuscated-path]/pnpm-monorepo/packages/backend-adapters/src/package.json' does not exist according to earlier cached lookups.
File '/[obfuscated-path]/pnpm-monorepo/packages/backend-adapters/package.json' exists according to earlier cached lookups.

I ran tsc --traceResolution in the package with moduleResolution: Node and got this:

======== Module name 'msw' was successfully resolved to '/[obfuscated-path]/pnpm-monorepo/node_modules/.pnpm/msw@1.3.2_typescript@4.9.5/node_modules/msw/lib/index.d.ts' with Package ID 'msw/lib/index.d.ts@1.3.2'. ========

Looks like it resolved successfully both times so not exactly sure what’s going on here.

Maybe you can try like this, post written in Chinese, I don’t guarantee it can solve your problem. https://juejin.cn/post/7282606413842415675

This is driving me nuts. Screenshot 2023-09-17 at 11 12 14 PM

Screenshot 2023-09-17 at 11 14 16 PM

Also confirmed that the BullMQ version we’re using is using that same version of ioredis. Unfortunately:

src/router.ts:35:14 - error TS2742: The inferred type of 'appRouter' cannot be named without a reference to '@/core/node_modules/ioredis/built/Redis'. This is likely not portable. A type annotation is necessary.

35 export const appRouter = mergeRouters(

This call is super distant, it’s like a second-level import. Bafflingly weird problem as I can’t even figure out where the mismatch is.

Edit: to anyone who is seeing this, the only thing that worked was to pull the package out to the root of the monorepo. Now every single package will install the dependencies, but at least now it’s all working end-to-end finally.

My case was:

Been working with pnpm workspace. My structure:

  • package.json
  • packages
  • users
    • package.json
  • wallet
    • package.json

The 3 package.json have the same package installed with a different version. Uninstalled the package from the parent package.json the problem was gone.

I’ve faced today this error in nx monorepo with pnpm, while playing and experimenting setting vite-plugin-node as node server, and started overuse different compiler options in the root tsconfig.base.json file. I’ve realized that setting composite key to true, started giving me that error, or in my case more specific:

The inferred type of 'apiGateway' cannot be named without a reference to '.pnpm/@types+express-serve-static-core@4.17.31/node_modules/@types/express-serve-static-core'. This is likely not portable. A type annotation is necessary.

@Dealerpriest not sure if u already tried the composite key set to false or removing it.

Since this has to do with hoisting, switching the function declarations from const to function resolved this for me. I encountered this when exporting the configurations for my redux store

Before:

The inferred type of ‘setupStore’ cannot be named without a reference to ‘XXX’. This is likely not portable. A type annotation is necessary.

export const setupStore = (preloadedState?: PreloadedState<RootState>) => {
  return configureStore({...});
}

After: (no errors)

export function setupStore (preloadedState?: PreloadedState<RootState>) {
  return configureStore({...});
}

In a Vue 3 app I commented out "composite": true under compilerOptions, because it sets declaration: true

I managed to get this working. The steps would be to install all troublesome node_modules in the root package.json, then add this (in my case) to tsconfig.json at the root

"paths": {
      "react": ["node_modules/@types/react"], // fair I am using this
      "react-query": ["node_modules/react-query"], // fair I am using this
      "csstype": ["node_modules/csstype"] // this is causing issues from within react using CSSProperties
    }

And this makes sense due to the nature of pnpm, it installs the actual files into node_modules/.pnpm/<package>@<version>/node_modules, then symlinks this into each workspace. You cannot guess this path, hence installing the packages into the root package.json will have them symlinked into the node_modules at the root, and you can reference them through paths.

I am not sure if this is a typescript issue or a pnpm issue, but I do think having to go through those steps is sub-optimal 😞

For my case, I’ve found the following:

It only occurs when "declaration": true (no idea why however)

You can work around it by either:

  1. Using paths:{} in compilerOptions
  2. Deleting the problematic package from the node_modules folder of the symlink’d location (not practical but it does fix the problem)
  3. Publishing and install via NPM

My understanding of declaration: true is that a *.d.ts file is created. It would be great to navigate directly to the *.ts source.

Also set declarationMap: true.