TypeScript icon indicating copy to clipboard operation
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.

Open mistic opened this issue 4 years ago • 61 comments

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

mistic avatar Feb 19 '21 00:02 mistic

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 avatar Feb 19 '21 01:02 weswigham

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

mistic avatar Feb 22 '21 16:02 mistic

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.

jarrodpayne avatar Jun 18 '21 17:06 jarrodpayne

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.

btakita avatar Jun 26 '21 18:06 btakita

 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.

weswigham avatar Jun 26 '21 19:06 weswigham

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?

btakita avatar Jun 26 '21 20:06 btakita

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.

weswigham avatar Jun 26 '21 20:06 weswigham

Check if this works for you: https://github.com/microsoft/TypeScript/issues/29808#issuecomment-540292885

nwaughachukwuma avatar Jul 22 '21 11:07 nwaughachukwuma

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

mistic avatar Jul 22 '21 12:07 mistic

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

riordanpawley avatar Jul 22 '21 12:07 riordanpawley

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.

dustinlacewell avatar Jul 23 '21 17:07 dustinlacewell

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.

jarrodpayne avatar Jul 23 '21 18:07 jarrodpayne

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

InsOpDe avatar Aug 26 '21 11:08 InsOpDe

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

3cL1p5e7 avatar Dec 16 '21 12:12 3cL1p5e7

In my case, add preserveSymlinks for resolve it

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

imcuttle avatar Jan 07 '22 16:01 imcuttle

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

zhyd1997 avatar Jan 31 '22 02:01 zhyd1997

In my case, add preserveSymlinks for resolve it

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

This solved it for me too, but why ? :thinking:

OoDeLally avatar Feb 04 '22 16:02 OoDeLally

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

andre-byrne avatar Feb 10 '22 23:02 andre-byrne

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.

tstordyallison avatar Feb 17 '22 20:02 tstordyallison

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

nemosmithasf avatar Mar 09 '22 01:03 nemosmithasf

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

af4oz avatar Mar 17 '22 09:03 af4oz

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

vjpr avatar Apr 27 '22 14:04 vjpr

I use pnpm.overrides remove the duplicate dependencies and resolved this problem.

wmzy avatar May 06 '22 06:05 wmzy

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

vtereshyn avatar May 19 '22 08:05 vtereshyn

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 preserveSymlinks to 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

jsheely avatar May 24 '22 01:05 jsheely

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 avatar May 24 '22 17:05 tbergquist-godaddy

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

jsheely avatar May 27 '22 12:05 jsheely

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 avatar May 27 '22 13:05 tbergquist-godaddy

@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

jsheely avatar May 27 '22 14:05 jsheely

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:

jsheely avatar May 27 '22 15:05 jsheely