Closed renke closed 1 week ago
I could look into it if you provide a minimal reproduction repository
I could look into it if you provide a minimal reproduction repository
Thanks.
Here it is in a semi-broken state: https://github.com/ivancuric/repro-pnpm-types
pnpm build
will build all the packages.
One step can be fixed by uncommenting:
// import type {} from "imagecapture-wasm";
// import type {} from "imagecapture-worker";
Weirdly enough, the apps/example-solidjs
package isn't complaining in this repo like the original one did:
The inferred type of 'workerRoot' cannot be named without a reference to '../../../packages/imagecapture-worker/node_modules/comlink/dist/umd/comlink'. This is likely not portable. A type annotation is necessary.ts(2742)
The inferred type of 'workerRoot' cannot be named without a reference to '../../../packages/imagecapture-worker/node_modules/imagecapture-wasm/types'. This is likely not portable. A type annotation is necessary.ts(2742)
I also noticed that what VScode's TS server shows isn't indicative of what the actual state is. It can show both false positives and negatives. Sometimes quitting helps when restarting the server or reloading the window fails.
@ivancuric I could fix your issue with workaround #3 ("intermediary interface"), see https://github.com/pkerschbaum/repro-pnpm-types/commit/d3cd550990301959d058a3df65531c9a57d25146.
I forgot to mention one thing for workaround #3: it must be an interface
, not a type
.
In the repo you provided, the broken code is:
// case #1, just reexport
export type RemoteObj = typeof exportedApi;
It stays broken when we do this:
// case #2, intersection type with empty object
export type RemoteObj = typeof exportedApi & {};
But it suddenly works when we do this:
// case #3, intersection type with interface
interface MyBlah {}
export type RemoteObj = typeof exportedApi & MyBlah;
That's of course very weird and suggests that there is a bug in TypeScript. I think in its inner workings, tsc "optimizes away" simple type aliases. Both case #1 and #2 can be simplified to just typeof exportedApi
, which tsc tries to put into the types of the package imagecapture-main
. But this leads - for valid reasons - to the This is likely not portable
error. Case #3 seems to be not simple enough so tsc keeps the RemoteObj
type around, solving the error.
Note that this is just my reasoning based on the observed behavior.
Throwing in my solution just in case someone else has the same problem
I had a symlinked package and that was causing the issue. Unlinking and relinking fixes the issue
@pkerschbaum Thanks a ton for the in-depth solution.
I'd like to add in another solution on-top of the
- Introduce an "intermediary interface" in the types of the dependency
It seems like I was able to accomplish the same thing by just re-exporting the inferred type from the intermediate, rather than having to create a whole new interface and exporting that. e.g.
diff --git a/index.d.ts b/index.d.ts
index 53dc74d77277c7fa4fd459caa7ed261bf3a0ea02..71532cbd99dca6bcf102c000345156d39950a5b0 100755
--- a/index.d.ts
+++ b/index.d.ts
@@ -21,6 +21,8 @@ import * as serveStatic from 'serve-static';
import * as core from 'express-serve-static-core';
import * as qs from 'qs';
+export { Express } from 'express-serve-static-core';
+
/**
* Creates an Express application. The express() function is a top-level function exported by the express module.
*/
@ivancuric This solution also works for your problem, where re-exporting the missing inferred type works rather than having to create a new interface
+export type { ImageCaptureBindings } from "imagecapture-wasm";
export type RemoteObj = typeof exportedApi;
@robin-pham you are right! I have split workaround 3 in two subparts and your suggestion is 3.1, the other 3.2 :)
@pkerschbaum thanks for the detailed write-up it certainly saved me some time.
I can confirm the re-export works. I am in a monorepo context with Yarn 3 and .yarnrc.yml
:
nmHoistingLimits: workspaces
nmMode: hardlinks-local
nodeLinker: node-modules
Which gives similar results to pnpm isolation.
The crux of this seems is that the 'resolution' used when generating declaration files is different to the way node resolution resolves compiled files, so we have:
So any package consuming that declaration file of
express-app
might not haveexpress-serve-static-core
installed.
But we don't usually need to introduce a direct dependency in order to consume transitive dependencies, but apparently for type declarations we do *under the conditions enumerated here: https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189).
I'd be interested to understand the technical limitation here and how fundamental it is or otherwise to the way types are resolved.
Do types for a module need to be flattened into the declaration file at the root of the import? I can see how this would become bloated.
One thing I tried that did not work was to re-export the module from the direct dependency of the module that was complaining, but only re-exporting it from the consuming module that was failing to compile worked, as in:
A --depends on--> B
Compiling A
gives:
The inferred type of 'Foo' cannot be named without a reference to 'B/node_modules/foo'. This is likely not portable. A type annotation is necessary
Re-exporting Foo
from B
does not work, re-exporting from A
from the file where the error is emitted does.
@pkerschbaum thanks for your write-up.
I'm currently trying to fix: https://github.com/drizzle-team/drizzle-orm/issues/656 However it seems all workarounds do not work, and the suggested workarounds in the issue, seem bad. Can someone help to find the root cause?
When using "moduleResolution": "node16"
, if we use a library that has exports
defined in package.json, it seems that we cannot import file unless the file is enumerated in exports
(Note that wildcard export did not work for me).
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.mjs",
"require": "./index.cjs",
"default": "./index.mjs"
},
"./additional-types": {
"types": "./additional-types.d.ts"
}
}
@takahashi-shotaro how did the wild card exports look?
I noticed that wildcards are evaluated only one way, but typescript needs to map two ways; the first mapping is when you try to import from x, But it also does a check if it can find an export entry based on the full file name. Typescript does not implement support for this reverse mapping check with wild cards, only statically defined entries.
which then causes the error, perhaps you run into this.
here https://github.com/microsoft/TypeScript/blob/84d8429fb623e0e89c1b59fe65d105eda094245f/src/compiler/moduleSpecifiers.ts#L863 is no matching mode specified, so it only supports the default: Exact
@patroza After applying your patch I can now export with wildcards, thanks.
@patroza After applying your patch I can now export with wildcards, thanks.
Guess we should make a PR
Hi, unfortunately we have exactly the problem with dynamic types (through Prisma ORM) in the context of monorepo workspaces and PNPM. Please provide a fix for this.
Hi, unfortunately we have exactly the problem with dynamic types (through Prisma ORM) in the context of monorepo workspaces and PNPM. Please provide a fix for this.
Us too, same tools!
@RyanCavanaugh can a typescript team member look into this?
.pnpm
folder introducing another node_modules
in the path, triggering the warning, workaround with
I'm encountering the same issue while using @mui/material and @emotion/styled. Currently, I need to manually declare types to resolve it. to chinese
I have read some of the suggested solutions in the comments, but they haven't been helpful for me. I'd like to know if this issue will be fixed in the future.
It's really annoying that even with correct exports, this seems to be not working for TS + pnpm + moduleResolution: 'NodeNext'
.
Will this be fixed in TS?
@robin-pham's fix worked for me too. My halfass writeup:
Error:
pnpm link ../B
foodtruck-js
{ FreeTacos }
from B/src/freetacos.ts
FreeTacos
uses a type inferred from foodtruck-js
FreeTacos
, but A does not import foodtruck-js
The inferred type of 'FreeTacos' cannot be named without a reference to 'B/node_modules/foodtruck-js'. This is likely not portable. A type annotation is necessary.ts(2742)
Fix:
export type * from 'foodtruck-js'
in repo B's index file (need to dig through which specific types are requiring the issue).foodtruck-js
as a package dependency in A.same problem when using pnpm monorepo
This error is because
declaration: true
intsconfig.json
and declaration is true whencomposite: true
. ๐
turning the feature off is not fixing the issue, its just ignoring it and disabling functionality
Maybe you can try like this, post written in Chinese, I don't guarantee it can solve your problem. https://juejin.cn/post/7282606413842415675
@RyanCavanaugh The issue is happening to me on npm monorepos as well.
It seems to me, that typescript is not recognizing the outermost node_module folder as part of the project. Am I missing some tsconfig setting that should be applied when working in monorepos?
I am getting the following error:
The inferred type of 'initializeModules' cannot be named without a reference to '../../../../node_modules/@yellow/core/dist/Classes/Mustache'. This is likely not portable. A type annotation is necessary.ts(2742)
This is how my package.json
file looks like:
{
"name": "@ratestay/server",
"version": "1.0.0",
"scripts": {
"build": "rm -Rf dist && tsc --project tsconfig.build.json --noEmit && BABEL_ENV=prod babel src --out-dir dist --copy-files --extensions '.ts,.js'",
"lint": "eslint src/** && tsc --noEmit --project tsconfig.json",
"typecheck": "tsc --project tsconfig.build.json --noEmit",
"dev": "nodemon",
"start": "node build/index.js"
},
"nodemonConfig": {
"exec": "BABEL_ENV=dev DEV_ENV=true NODE_OPTIONS='--conditions=developement' babel-node -x .ts,.js --ignore '*\/node_modules\/*' src/index.ts",
"ext": ".ts",
"delay": "500",
"watch": [
"./src/*",
"./../../../packages/*"
]
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/nodemailer": "^6.4.10",
"@yellow-utils/babel": "^1.0.0",
"@yellow-utils/eslint-config": "^1.0.0",
"@yellow-utils/tsconfig": "^1.0.0",
"nodemon": "^3.0.1",
"type-fest": "^4.3.1",
"typescript": "^5.2.2"
},
"dependencies": {
"@yellow/backend": "*",
"@yellow/core": "^1.1.6",
"express": "^4.18.2",
"lodash": "^4.17.21",
"nodemailer": "^6.9.5",
"sequelize": "^6.32.1"
}
}
And this is my tsconfig.json
file:
{
"extends": "@yellow-utils/tsconfig/tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"paths": {
"@yellow/backend": ["./../../../packages/@yellow/backend/src/index.ts"],
"@yellow/backend/sequelize": ["./../../../packages/@yellow/backend/src/exports/sequelize.ts"],
"config": ["./src/config/config.local.ts", "./src/config/config.ts"]
}
},
"references": [
{"path": "./../../../packages/@yellow/backend/tsconfig.json"}
],
"include": ["./src/**/*"],
"exclude": ["node_modules/"]
}
If you say that this should work on npm
monorepos, than maybe I am having some configuration problems, that can solve this.
For reference, I am including the extended config as well:
// @yellow-utils/tsconfig/tsconfig.base.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"checkJs": false,
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"noImplicitAny": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"resolveJsonModule": true,
"preserveSymlinks": false,
"skipLibCheck": true,
"strict": true,
"strictBindCallApply": false,
"strictNullChecks": false,
"strictPropertyInitialization": false,
"strictFunctionTypes": false,
"useUnknownInCatchVariables": false,
"target": "es6"
}
}
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:
4.9.5
1.3.2
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.
I'm on TS 5.2.2 & pnpm, suddenly started happening
I'm on TS 5.2.2 & pnpm, suddenly started happening
Same here, just started seeing it all of a sudden. strange
Same here for Version 5.3.3
same for me, "typescript": "^5.3.3"
and pnpm 8.11.0
same here
same here
Several years later, the problem still persists
same here.
FYI I posted a comment with 7 workarounds a while ago (link), and all of them apply also to the latest TypeScript version (5.3.3).
Same here with typescript 5.3.3 and pnpm 8.14.0
I've been experiencing this same issue
The inferred type of storyFactory cannot be named without a reference to .pnpm/@intlify+core-base@9.8.0/node_modules/@intlify/core-base . This is likely not portable. A type annotation is necessary.
and the way I've worked around it was to add this, although it would be nice if this issue gets fixed since it has been present for years
๐
declare module '@intlify/core-base' {}
I've been experiencing this same issue
The inferred type of storyFactory cannot be named without a reference to .pnpm/@intlify+core-base@9.8.0/node_modules/@intlify/core-base . This is likely not portable. A type annotation is necessary.
and the way I've worked around it was to add this, although it would be nice if this issue gets fixed since it has been present for years
๐
declare module '@intlify/core-base' {}
Interesting, didn't hear about this workaround yet!
I tried it out and unfortunately, the emitted declaration files are broken then. I added it as workaround #8 here, you can check it out https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189.
Started happening all of a sudden as well with no explanation like others have experienced in this issue (TS 5.3.3, pnpm 8.14)
My problem is rather confusing
This works
// tailwind.config.ts
import { themeVariants } from 'tailwindcss-theme-variants'
import { type Config } from 'tailwindcss'
const config: Config = { /* ... */ }
export default config
This doesn't work
const config = { /* ... */ } as const satisfies Config
I would prefer the inferred version due to infinitely better intellisense. What options do I have?
The project I was working on extended a config that had "declaration": true
, which was causing this.
My problem was a stupid Windows-Problem: the error occurred in a jenkins build and the root-cause of the error was a too long path.
In our case we have an Angular module built with the maven frontend-plugin. The path, where the build was executed, was
C:\Jenkins\workspace\vgwort\JERRY\Release Management\Release eines Moduls\target\checkout\hf.webclient
and the error was
18:07:13 [INFO] [INFO] projects/jerry/hf-init/src/lib/hf.menu.ts:20:14 - error TS2742: The inferred type of 'HF_MENU' cannot be named without a reference to '.pnpm/@angular+core@15.2.1_rxjs@7.8.0_zone.js@0.11.4/node_modules/@angular/core'. This is likely not portable. A type annotation is necessary.
Executing the build in
C:\Jenkins\workspace\vgwort\JERRY\Release Management\Release eines Moduls\hf.webclient
works fine. Maybe this helps....
FYI I posted a comment with 7 workarounds a while ago (link), and all of them apply also to the latest TypeScript version (5.3.3).
Do we have any data around which of these workarounds are most reliable?
I'm experiencing the same issue with pnpm + Vue 3 + Pinia, using TanStack Query in a defineStore
call, and so far, I haven't found a workaround that's worked yet. It could be that I'm importing the wrong types or using the wrong paths in said workaround, but alas. This also happened briefly with Vue Use's useFetch
hook in the same store, and unfortunately I don't remember how I resolved it previously. It wasn't until I swapped it for useQuery
that I got the error again.
I am having this issue as well, on v5.3.3.
While removing "declaration": true
makes the issue go away, I am writing a module that I plan to publish on NPM, so disabling the output of declaration files is quite simply not an option, seeing as the module will not function without those files. Quite similar to how TypeScript complains if you don't have the @types/X
package installed for a legacy package (i.e. one that doesn't include its own typings).
In a simple class that wraps around the client created by node-redis
:
Makes it impossible to use Redis in the following way, where you can have tons of custom scripts/commands that are very difficult if impossible to correctly reflect using the package's included types, and not using typeof client
at runtime as suggested here:
This code alone generates 137 TS compiler errors, all in the format shown in the first screenshot. It's very unfortunate that this has been an ongoing issue for so long. It feels like I have hit a brick wall, as this one error essentially renders TS unusable. Every workaround that compiles involves stripping varying levels of contextual data from the type (such as explicitly setting the type of client to any
), which undermines the entire point of using TypeScript in the first place.
I am having this issue as well, on v5.3.3. ... This code alone generates 137 TS compiler errors, all in the format shown in the first screenshot. It's very unfortunate that this has been an ongoing issue for so long. It feels like I have hit a brick wall, as this one error essentially renders TS unusable. Every workaround that compiles involves stripping varying levels of contextual data from the type (such as explicitly setting the type of client to
any
), which undermines the entire point of using TypeScript in the first place.
@Threebow I spent some time trying to solve it for the redis
package but wasn't succesful.
However, you can fix this by installing & using @redis/client
(and other redis packages you rely on) directly instead of redis
/ node-redis
.
Good luck!
Same problem when building a react library alongside styled-components
with tsup
Same problem here
@ivancuric I could fix your issue with workaround #3 ("intermediary interface"), see https://github.com/pkerschbaum/repro-pnpm-types/commit/d3cd550990301959d058a3df65531c9a57d25146.
I forgot to mention one thing for workaround #3: it must be an
interface
, not atype
. In the repo you provided, the broken code is:// case #1, just reexport export type RemoteObj = typeof exportedApi;
It stays broken when we do this:
// case #2, intersection type with empty object export type RemoteObj = typeof exportedApi & {};
But it suddenly works when we do this:
// case #3, intersection type with interface interface MyBlah {} export type RemoteObj = typeof exportedApi & MyBlah;
That's of course very weird and suggests that there is a bug in TypeScript. I think in its inner workings, tsc "optimizes away" simple type aliases. Both case #1 and #2 can be simplified to just
typeof exportedApi
, which tsc tries to put into the types of the packageimagecapture-main
. But this leads - for valid reasons - to theThis is likely not portable
error. Case #3 seems to be not simple enough so tsc keeps theRemoteObj
type around, solving the error.Note that this is just my reasoning based on the observed behavior.
The fact that intersecting a type with an interface would make it suddenly works like a miracle indeed suggests something interesting. I'm not sure if @RyanCavanaugh @DanielRosenwasser has seen this hint, but if you haven't, please prioritize.
I have managed to make the error go away by making a TypeScript Reference Type at the top of the file
In my case I was was importing useI18n
function and spreading an object in it's parameter object like
useI18n({ ...someObj })
which caused this
The inferred type of useI18n cannot be named without a reference to .pnpm/@intlify+core-base@9.9.0/node_modules/@intlify/core-base . This is likely not portable. A type annotation is necessary.
โ Fixed it by doing this
At the top of the file which used useI18n
I've added the TS reference type like so
/// <reference types="@intlify/core-base" />
So to fix it, basically check the problematic package name ( in my case it was @intlify/core-base
) and add it as a TypeScript Reference Type like in the example above
use type casting it is always solved in that way
import type {} from "Y";
This didn't work for my Nx
monorepo with pnpm
because in the workspace, peer dependencies were installed inside the library folder duplicating the package and causing the error. I opted to disable the peerdeps installation with:
.npmrc
auto-install-peers=false
and reviewed the peer dependencies warnings to confirm that they were installed, and added the ones missing: then I confirmed that everything ran as expected.
FYI there is some interesting discussion on this topic in microsoft/TypeScript#58176.
I find this idea for a solution by @eps1lon interesting (which is kind of bringing workaround #3.1 of my comment above into tsc itself):
Is there an option to enforce that whatever
getMakeSubDep
returns, is also exported fromother_package
? Then TypeScript could reference those exports instead. It should already do that today if you explicitly export the return type. But TypeScript favorsimport('sub_dep').Options
overimport('other_package').Options
even ifother_package
explicitly exportsOptions
.
ๆจ็้ฎไปถๆๅทฒๆถๅฐ๏ผย ๆไผๅจ็ฌฌไธๆถ้ดๅ ๅๅคๆจ๏ผ้ๅธธๆ่ฐข๏ผ
Bug Report
๐ Search Terms
๐ Version & Regression Information
Problems occurs with 4.5.x and 4.6.x and most likely earlier versions (I've tried a few other versions). It stills occurs on
4.7.0-dev.20220321
.โฏ Playground Link
I've created minimal repository that shows the problem: https://github.com/renke/typescript-package-id-merge-repro
The problem occurs when using pnpm (due to the modules layout it uses). No problems occur when using npm/yarn.
To reproduce the problem run
pnpm install
and thenpnpm check
pnpx tsc -b
.๐ป Code
I don't think the code itself matters to much except from the fact that it in fact does not have explicit type annotations (which is kind of the idea when using zod and by extension @renke/vommer).
๐ Actual behavior
The following error occurs when trying to build a composite TypeScript project (same happens when just using
declaration: true
).The dependency tree of
@renke/vommer
Looking at the resolution trace TypeScript tries to resolve
@renke/vo
two times the first time from@renke/vommer
and the second time from@renke/vod
. Both end up having the package ID@renke/vo/dist/index.d.ts@0.2.0
.Using
"preserveSymlinks": true
doesn't solve the problem in so far that the error disappears but the type is inferred as any, because the dependencies of@renke/vommer
are not found. Also I don't actually want to use it.๐ Expected behavior
The error should not occur when there are two (or more) modules that have the same resolved package ID. It would make sense for the error to occur when they have different versions.