Angular2 - root relative imports
I have a problem with imports in angular2/typescript. I'd like to use imports with some root like 'app/components/calendar', instead only way I am able to use is something like:
//app/views/order/order-view.ts
import {Calendar} from '../../components/calendar
where Calendar is defined like:
//app/components/calendar.ts
export class Calendar {
}
and this obviously gets much worse the lower in hierarchy you go, deepest is '../../..' but it is still very bad and brittle. Is there any way how to use paths relative to project root?
I am working in Visual Studio, and relative imports seem to be the only thing that makes VS able to recognize these imports.y
Solution 1:
Answer
As of TypeScript 2.0 you can set tsconfig.json properties baseUrl
as following:
{
"compilerOptions": {
"baseUrl": "app"
}
Then, you might use your desired manner of importing components, like:
import { Calendar } from 'components/calendar';
Appendix
Fallback paths resolution
An important consideration is that specifying baseUrl
option causes TypeScript compilator to:
- Look up a path with regards to baseUrl
- On failed resolution (module not found), look up a path with regards to
moduleResolution
option
SystemJS
Since SystemJS is heavily used in Angular development stage, be sure to accordingly config also systemjs.config.js
, so that it resolves paths correctly.
Source and further details: https://github.com/Microsoft/TypeScript/issues/5039
Solution 2:
UPDATE
Just don't use this solution. Embrace node module resolution algorithm. Community rests on it, so everything will break apart if you try to do otherwise. Use aliases or some of the other provided solutions.
Short answer
There's a way but you shouldn't do it.
Set the compilerOption "moduleResolution"
to "classic"
.
Long answer
Are you using tsconfig.json
? I assume you are. I've been looking a way to make statements such as import someModule = require ("../../../../someModule"
into import someModule=require("src/path/to/someModule")
.
I found after hours wasted that tsc may use different algorithms for module resolution. I'm using atom and it creates the tsconfig.json
with the compilerOption property moduleResolution
set to "node"
and it uses the shitty (excuse my french) module resolution algorithm of node. I just put "classic" and started working the obvious way.
Solution 3:
Basically, in Angular 6
you can start the import
from the top or bottom.
My two options
They are available with default config generated by angular CLI
-
From the top
I prefer this way if it's closer from the root of the appimport { DateService } from "src/app/shared-services/context/date.service";
-
From the bottom
import { DateService } from "../../../../../../../shared-services/context/date.service";
Context:
TypeScript config: tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
Angular's stack
ng -v
Angular CLI: 6.0.8
Node: 8.9.0
OS: win32 x64
Angular: 6.0.7
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.6.8
@angular-devkit/build-angular 0.6.8
@angular-devkit/build-optimizer 0.6.8
@angular-devkit/core 0.6.8
@angular-devkit/schematics 0.6.8
@angular/cli 6.0.8
@ngtools/webpack 6.0.8
@schematics/angular 0.6.8
@schematics/update 0.6.8
rxjs 6.2.1
typescript 2.7.2
webpack 4.8.3
Solution 4:
One way would be to have files that re export and bundle the files with a shorter path.
You could have a components.ts folder in the root of your application with.
export {Calendar} from './components/calendar'
export {*} from './components/map'
And importing it from components
import {Calendar, Map} from '../../components';
This however will is better suited to exporting modules so others can use them, than the way to structure a project.
The alternative would be to forgo the use of import statements and use internal modules instead.
calendar.ts
module Components {
export class Calendar {}
}
And you will be able to just use it in any of the files in your projects like this.
new Components.Calendar();
Or import it with an alias.
import Calendar = Components.Calendar;
new Calendar();
Solution 5:
Tl;dr: you should now be able to use src
as the root and it will "just work".
With Angular 2.0, when you run ng new my-proj
, it auto-creates a file my-proj/src/tsconfig.app.json
. This file contains a baseUrl
line:
"baseUrl": "./",
Thus, you shouldn't have to change anything about your config and you can scope all of your imports rooted on src
. Assuming your project structure looks something like:
my-proj/
README.md
src/
app/
sidebar/
sidebar.component.ts
user.service.ts
you can import your files with:
import { Sidebar } from 'app/sidebar/sidebar.component';
import { UserService } from 'app/user.service';
Edited to add: Sublime does not read src/tsconfig.json
, though, it only reads the top-level tsconfig.json (see this issue). If you're getting Cannot find module 'app/...'
errors on your import lines in Sublime, you can add:
"baseUrl": "./src",
to your top-level tsconfig.json file.