TypeScript
TypeScript copied to clipboard
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 [email protected]. 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:
- https://github.com/microsoft/TypeScript/issues/30858
- https://github.com/microsoft/TypeScript/issues/28689
- https://github.com/microsoft/TypeScript/issues/2338
- https://github.com/microsoft/TypeScript/issues/29221
⏯ 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
withRouterwithinreact-router-domthat is just a plain export from the same type onreact-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.
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.
@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'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.
I have a set of npm packages where "types" points to a *.ts (not a *.d.ts) file. Reason being I can directly jump to the implementation with the IDE. The issue I'm running into is the following:
An npm package B that depends on another npm package A. A factory function obj_ that returns a value with a custom type defined or referenced by pkg A. pkg B has export const obj = obj_(). Typescript compilation is successful. When another package C imports the obj from pkg B, The inferred type of "obj" cannot be named without a reference to "path/to/pkg/A". This is likely not portable. A type annotation is necessary.
This cannot happen. 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. Otherwise, the pkg B has to be fully built & installed by another pkg C as an npm package to test. Inferring the type across packages (not in rootDir) would be the more ergonomic solution, as long as performance does not suffer.
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.
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?
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.
Check if this works for you: https://github.com/microsoft/TypeScript/issues/29808#issuecomment-540292885
@weswigham @btakita not related with that initial issue but with your last discussion: declarationMap has also a buggy behaviour when used along sourceRoot (which is vital in some cases) https://github.com/microsoft/TypeScript/issues/31873 we are still waiting for a solution on that other issue.
Check if this works for you: #29808 (comment)
doesn't help in my case. yarn workspaces monorepo setup with monorepo/pkg1 depending on monorepo/pkg2 & node_modules/pkg1 monorepo/pkg2 depending on node_modules/pkg1 Using same version of external/pkg1
I get this issue in a Rush monorepo where a project depends on cmd-ts. Strangely, it only affects VSCode. I have to do the path alias trick, restart TS, but then it works. An I can remove the path alias. Very strange.
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.
Im using a pnpm monorepo and had a similiar problem which I could resolve by setting
baseUrl: "." inside the package tsconfig.json
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 :)
In my case, add preserveSymlinks for resolve it
{
"compilerOptions": {
"preserveSymlinks": true
}
}
@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!
In my case, add
preserveSymlinksfor resolve it{ "compilerOptions": { "preserveSymlinks": true } }
This solved it for me too, but why ? :thinking:
Is there any way to designate parts of your code where this error is ignored? I'm getting this error, but in a part of my code that will never be exported for an end user (in private code, I mean).
FWIW, I'm getting this when compiling https://github.com/facebook/flipper on Windows, where I have my code on drive ("I:") that is actually just symlinked (or whatever that is on Windows) to a directory on the "C:" drive.
Running the compile from the actual "C:" location makes this error go away. Strange.
Using preserveSymlinks didn't make any difference for me.
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:
- Using
paths:{}incompilerOptions - Deleting the problematic package from the
node_modulesfolder of the symlink'd location (not practical but it does fix the problem) - Publishing and install via NPM
I removed "declaration": true , "preserveSymlinks": true" and restart ts server
I'm using pnpm and I made sure that I only had one version of react-query (in my case) across the entire codebase and it started working, restarted TS server, and added declarationMap: true. So one of these fixed it...
I use pnpm.overrides remove the duplicate dependencies and resolved this problem.
"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.
I was able to setup my repository so that it works with PNPM and NPM for installation in a monorepo workspace. I've found that this bug happens with PNPM but goes away with NPM
root
- packages
- apps
Appears to be a bug in dependency resolution. Appears to be related to how PNPM does symbolic folder links. This specifically happens when I expose a type from a child dependency.
As long as all the types in the sub module are internal to that module it works fine. But if I reference a type from a sub/sub module and then build it will fail
For science I've also tried pnpm i --shamefully-hoist and it fails the same.
None of the band aides mentioned above have seemed to resolve it for me using PNPM.
- I've tried setting the
preserveSymlinksto true or false as others have suggested. - My configuration already has
baseUrl: "." - Setting paths in
tsconfig.json
Obviously disable declaration resolves the build. But the whole point is to create the index.d.ts declaration file that is blowing up
It fails consistently in all forms:
- VS Code Intellisense
tsc index.ts --declaration --emitDeclarationOnly --allowSyntheticDefaultImports --outfile dist/index.d.ts- And using Rollup bundler with the rollup-plugin-dts
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 😞
@tbergquist-godaddy Not sure I understand. In my setup I'm referencing a package (A) within my mono repo into another package (B) in the same folder.
(A) References a third party dependency that is the one being referenced as an error in (B) when I import A and use it
In my case PNPM installs a symbolic link of (A) into (B) node_modules. (A) then contains a node_modules symbol link to the third party package
But as far as TSC should be concerned this is standard dependency hierarchy and it should be able to find all the typings.
Well, from the error I get The inferred type of 'useSaveLanguage' cannot be named without a reference to 'packages/whitelabel/node_modules/react-query/types. And this error is coming from packages/language/.... which gets its types from packages/language/node_modules/react-query/types.
Both of these might be symlinked to the same type, but I take it that tsc doesn't follow symlinks and see the paths as different path and then types.
@tbergquist-godaddy Oh that's interesting that yours references the folder path fully. Mine references the module name then path
The inferred type of 'Ethereum' cannot be named without a reference to '@decentology/hyperverse-evm/node_modules/@rainbow-me/rainbowkit'. This is likely not portable. A type annotation is necessary.
It's frustrating that this problem goes away when I use npm install beause while I don't get this error I have other issues that require that I use PNPM. So it leads me to believe that it's just a setting away in PNPM to resolve the error
Upon further trial and error. I can confirm that setting the path in tsconfig.json does in fact work
However I'm not sure I understand why
Here is how I fixed it:
I created a .npmrc in the package (B) that was having the problem and set. Which is nice because I can localize what needs hoisted per package: Reference: https://pnpm.io/npmrc#public-hoist-pattern
EDIT: I thought .npmrc in sub folder worked. Seems that may have been a fluke. Adding it to root .npmrc more reliable
public-hoist-pattern[]=*@rainbow-me*
public-hoist-pattern[]=*@ethersproject*
Which were the two projects that had errors with the The inteferred type....
I then ran pnpm i to install the modules. This lifted thw two packages into the root mono repo node_modules folder
I then went back to package (B) and configured the tsconfig.json as others have suggested to point to that path. Reference: https://www.typescriptlang.org/tsconfig#paths
"paths": {
"@rainbow-me/rainbowkit": ["../../node_modules/@rainbow-me/rainbowkit"],
"@ethersproject/providers": ["../../node_modules/@ethersproject/providers"],
}
No more error. Yay!
So it's worth noting that the symbolic link path originally would have been nested like
node_modules/@decentology/hyperverse-evm/node_modules/@rainbow-me/rainbowkit
But when setting that nested path in my trial and error in the tsconfig.compilerOptions.path it fails to resolve the typings. But hosting it to the top resolves the problem. I assume the nested path would be the default path it would be using :shrug: