angular-cli: tsconfig paths and module resolution errors in VS Code

Bug Report or Feature Request (mark with an x)

- [x] bug report -> please search issues before submitting
- [ ] feature request

Versions.

@angular/cli: 1.4.9 node: 7.9.0 os: darwin x64 @angular/animations: 4.4.5 @angular/common: 4.4.5 @angular/compiler: 4.4.5 @angular/core: 4.4.5 @angular/forms: 4.4.5 @angular/http: 4.4.5 @angular/platform-browser: 4.4.5 @angular/platform-browser-dynamic: 4.4.5 @angular/router: 4.4.5 @angular/cli: 1.4.9 @angular/compiler-cli: 4.4.5 typescript: 2.3.4

macOS Sierra 10.12.6 VS Code 1.17.2

Repro steps.

My tsconfig path property seems broken. When I replace my path mapped imports (@shared, @assets etc.) with the equivalent coded relative paths, typescript module resolution seems to work (vs code intellisense is happy) I have a large angular 2 project and am used to aliasing my asset and shared directories this way. NOTE: project builds and (seems) to run just fine This has been reported often, but I can’t make sense of the current state of this issue. This more than annoying as VS Code reports Angular spurious errors on all of my components. I also suspect this is why I get the infamous Can’t read property isSkipSelf of null when I open my templates (html) in VS Code. I was running 1.4.2 and just tried ~1.4.2, which resolves to 1.4.9 in my package.json, to no avail. Will 1.5.x fix this? How can I help debug this without providing a copy of my app?

Thanks in advance. Regards.

The log given by the failure.

Desired functionality.

Mention any other details that might be useful.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 41
  • Comments: 42 (5 by maintainers)

Commits related to this issue

Most upvoted comments

@mehs2690 can you type environment in editor ( don’t add import statement yet ) and then press Ctrl + . when cursor in environment to see what option vscode suggest? Remember that because tsconfig.json and tsconfig.json has different baseUrl so you have to align path config accordingly

here is my config: tsconfig.json

"baseUrl": "./",
    "paths": {
      "@app/*": ["src/app/*"],
      "@env/*": ["src/environments/*"],
      "@views/*": ["src/app/views/*"]
    }

tsconfig.app.json

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    "module": "es2015",
    "types": [],
    "paths": {
      "@app/*": ["app/*"],
      "@env/*": ["environments/*"],
      "@views/*": ["app/views/*"]
    }
  },
  "exclude": ["test.ts", "**/*.spec.ts"],
  "angularCompilerOptions": {
    "annotationsAs": "static fields",
    "annotateForClosureCompiler": true,
    "preserveWhitespaces": false,
    "fullTemplateTypeCheck": true
  }
}

I had the same issue but it seams it was resolved with using a more “explicit” relative path in baseUrl in tsconfig.tswith "./src"

Before:

{
    "compilerOptions": {
        "baseUrl": "src",
        "paths": {
            "@app/*": ["app/*"],
        }
    }
}

After:

{
    "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
            "@app/*": ["app/*"],
        }
    }
}

vscode seem to only look into nearest tsconfig.json file, while angular read your tsconfig.app.json You should add relative path config to both tsconfig.json and tsconfig.app.json.

Simply restarting VSCode fixed this issue for me after updating the paths in tsconfig.json.

I had to set module_resolution in compiler options to get this to work for me

{
    "compilerOptions": {
      "moduleResolution": "node",
      "baseUrl": "./",
      "paths": {
        "@/*": ["src/*"]
      }
}

I am having this issue as well and I am unable to install Typescript Hero as access is restricted from where I work.

For me I only get the issue in test specs.

In app.module.ts this is fine:

import { AppRoutingModule } from '@app';
import { AppComponent } from '@app';
import { SharedModule } from '@shared';

but in app.component.spec.ts I get the error here:

import { AppComponent } from '@app';

[ts] Cannot find module '@app'.

I have 3 tsconfig files:

tsconfig.json in src:

{
    "extends": "../tsconfig.json",
    "compilerOptions": {
        "outDir": "../out-tsc/app"
    },
    "exclude": [
        "test.ts",
        "**/*.spec.ts"
    ]
}

tsconfig.spec.json in src:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/spec",
    "types": [
      "jasmine",
      "node"
    ]
  },
  "files": [
    "test.ts"
  ],
  "include": [
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}

and tsconfig.json in root:

{
    "compilerOptions": {
        "baseUrl": "src",
        "paths": {
            "@app": [
                "app/"
            ],
            "@ui": [
                "app/ui/"
            ],
            "@shared": [
                "app/modules/shared/"
            ]
        }
    }
}

When I am in app.module.ts and I do > Typescript: Go to project configuration I get taken to src/tsconfig.json. When I am in app.component.spec.ts and do the same I get a message saying Files in not part of a TypeScript project. When I go to configure tsconfig.json I get taken to the tsconfig.json in the root of the project.

This seems to be more of a VSCode issue than an angular issue so I will post this as an issue in VSCode.

The basic problem here is that by default when the tools in TypeScript toolset try to determine which compiler configuration to use with any given file, they look for a file named tsconfig.json starting in the directory where the TS file is located. If it is not there, they recursively looks up the file system until they find a tsconfig.json file. They do not look for tsconfig.app.json, tsconfig.spec.json or tsconfig.lib.json or any other name.

The default can be overridden. Presumably, that’s what angular-cli does when it invokes the TypeScript compiler or tslint. It uses -p or an API equivalent to pass it’s specially named configuration files.

However, this falls apart when it comes to getting editors to automatically recognize those files. TypeScript editors that provide complete support for TypeScript, like VSCode, usually launch a tsserver instance. And tsserver being part of the TypeScript toolset, does its default thing when the editor request that it processes a file: it looks for a file named tsconfig.json. I’ve used tsserver in dozens of projects (including Angular projects that don’t use angular-cli), and got it to find the proper tsconfig.json in all cases. (Of course the project has to be structured to take into account the default behavior of the TS toolset.) It only fails in projects that use angular-cli to generate configuration, due to the non-standard configuration file names that angular-cli uses.

An issue with the TypeScript project was opened here but it does not look like any modifications will be made to the TS toolset to handle angular-cli’s unusual naming convention. I’ve not found a newer issue that indicates otherwise.

Has there been any progress on this? Probably a VS Code issue rather than an Angular one. I can still replicate with the latest versions of each though. New cli project, trying to alias my src/app/ imports so they can be absolute (e.g. I want to be able to import @app/users/models/user.models.ts anywhere rather than ../../../users/models/user.models.ts with different relative paths where it is used).

I’ve tried adding "paths": { "@app/*": ["src/app/*"] } to the root tsconfig.json and it compiles but VS Code still shows a ‘cannot fine module’ error on the absolute import

can you try with Typescript Hero plugin? here is my screenshot screenshot from 2018-01-13 14 08 55

We use non-standard names for the each project tsconfig because there are files for two different compilations contexts side by side in the same folder. These are your app TS files, and your unit test TS files.

The unit test files have different typings available (jasmine, node) that really shouldn’t be valid in the app files. The app and unit tests also have different files/include/exclude arrays. So as far as compilation units are concerned, these definitely need to be different files.

What we did at the time was have a single top-level tsconfig file that was extra permissive, and local configs with different names on purpose so they weren’t caught by the editor. This way you wouldn’t get errors in your editor when you tried to use jasmine helpers in your unit tests.

At the time there weren’t a lot of Angular CLI based monorepos and that solution seemed good enough.

Now there are more. I think the strictly correct approach for these cases is to:

  • keep the toplevel tsconfig.json as is
  • add a new tsconfig.json on each project that extends the toplevel one, and contains a paths entry for that project
  • have tsconfig.app.json and tsconfig.spec.json in each project extend the tsconfig.json described above, and not define any paths themselves

So for it would look like this for a new workspace:

src/
--tsconfig.json       // paths go here
--tsconfig.app.json   // no paths here
--tsconfig.spec.json  // no paths here
tsconfig.json	      // no paths here

And for a monorepo style workspace:

projects/
--an-app/
----tsconfig.json
----tsconfig.app.json
----tsconfig.spec.json
--another-app/
----tsconfig.json
----tsconfig.app.json
----tsconfig.spec.json
--and-another-app/
----tsconfig.json
----tsconfig.app.json
----tsconfig.spec.json
tsconfig.json

We didn’t do this from the start because it was another file, and we were trying to keep the file count as low as we could. But maybe we should do this now when adding a new project.

@johankvint, your solution of using ./src worked for me. I no longer need to use the TypeScript Hero extension either.

Something to watch out for if you use barrel files there is different setup. Let’s suppose you have a guards folder in your app and your usual import is import * as fromGuards from '../../../guards';. I have found that I need to setup the following compilerOptions in order to get the desired effect. I’m not pleased with having to duplicate so much of the path, so please let me know if anyone has a better way to handle the barrels.

{
    "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
            "@app/guards": ["app/guards/"],
            "@app/guards/*": ["app/guards/*"]
        }
    }
}

If you have app/guards/index.ts and a LoginGuard component you should now have the following options to import.

import * as fromGuards from '@app/guards';

or

import {LoginGuard} from '@app/guards/login.guard';

Here is how I fixed it, turns out it is a problem with VSCode. Sometimes changes to tsconfig are not reflected in the IntelliSense. I closed VSCode and made sure no VSCode process was running. They reopened VSCode and Voila! IntelliSense now detects the @ 😩

Having problems as well. It does compile fine, but vscode cannot find it.

Discussed this in a meeting earlier and tsconfig “solutions” can be a great answer to this problem. We’ll do some more investigation to see exactly how this can work and what release we can fit it into.

There are many folders in my VSCode workspace inspector, and VSCode may only use the first folder’s tsconfig.json. If your tsconfig.json is not in the first folder, drag the folder to the first and try it again, it worked for me.

So one project one workspace and place the configure folder at first place.