19

Consider a simple typescript project with the following directory structure:

|   package.json
|   tsconfig.json
|               
\---src
    |   app.ts
    |   
    \---foobar
            Foo.ts
            Bar.ts

tsconfig.json has been configured to have ./src/ be the baseUrl.

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outDir": "./dist/",
        "baseUrl": "./src/"
    },
    "include": [
        "./src/**/*"
    ],
    "exclude": [
        "node_modules"
    ]
}

Now suppose we want to import Foo in Bar.ts. My understanding is that by setting the baseUrl, we can now use absolute paths to import modules

import { Foo } from 'foobar/Foo'

as opposed to relative paths

import { Foo } from './Foo'

If my understanding is correct, the typescript compiler should be able to automatically resolve foobar/Foo to ./Foo when compiling Bar.ts.

import { Foo } from 'foobar/Foo';

export class Bar {
  foo: Foo;

  constructor(num: number) {
    this.foo = new Foo(num);
  }
}

Running tsc compiles without errors. Yet, when we actually look at the compiled Bar.js, we would see that the path has not been resolved correctly, which would give us a Cannot find module error if we were to run it.

"use strict";
const Foo_1 = require("foobar/Foo");
class Bar {
    constructor(num) {
        this.foo = new Foo_1.Foo(num);
    }
}
exports.Bar = Bar;

So my question is: How can I get tsc to correctly resolve the absolute paths when importing modules using baseUrl? Or if this is not something that can be done, what is the purpose of baseUrl then?

Zsw
  • 3,312
  • 3
  • 21
  • 41

3 Answers3

4

The problem is that your module loader does not know how to find the module given the absolute path foobar/Foo.

The TypeScript compiler (tsc) is resolving the module paths correctly, otherwise you would get compilation errors. But it is trusting you to configure your module loader appropriately.

For example, from the documentation for RequireJS:

Supported configuration options:

baseUrl: the root path to use for all module lookups.

The TypeScript documentation talks a little about why you might need baseUrl:

Using a baseUrl is a common practice in applications using AMD module loaders where modules are “deployed” to a single folder at run-time. The sources of these modules can live in different directories, but a build script will put them all together.

Seamus
  • 3,733
  • 2
  • 30
  • 39
  • 1
    Thanks! So in other words, `tsc` isn't responsible for converting absolute path to relative path, and I'm supposed to configure the module loader to resolve it instead? If that is the case, is there a way to get `tsc` to simply convert to relative path instead? – Zsw Feb 21 '17 at 17:29
  • No, as far as I know, `tsc` won't convert paths for you. If possible, it will be easier to use paths in your `import` statement that conform to your module loader spec/config, and then use the various compiler options to tell `tsc` how to resolve the module names accordingly. – Seamus Feb 21 '17 at 17:46
  • 1
    @Zsw As an alternative, checkout the "outFile" compiler option. It will concatenate the modules into a single output: https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#concatenate-amd-and-system-modules-with---outfile – Seamus Feb 21 '17 at 19:10
  • Hi there, I'm aware this question is a bit old, but how do you "know" which module for code generation you want ? I have the same problem but I want my output to be in another directory and conserving the original structure of the project. – Jacks Apr 26 '17 at 12:45
  • 6
    You can use `NODE_PATH=dist/ node dist/app.js` so that node can also resolve absolute paths. – Denis Pshenov Feb 26 '18 at 14:15
  • @DenisPshenov thanks for the tip, this is what solved the issue – Tekz Jul 20 '18 at 09:26
  • @DenisPshenov Thank you! I can't believe the answer is buried in the comments. – Johnny Oshika Jan 24 '21 at 05:12
3

The answer comes from @DenisPshenov's comment in one of the answers. It's buried, so I'll provide it here...

Tell Node where the base url is with NODE_PATH environment variable so that it can resolve absolute paths:

Linux / macOS

NODE_PATH=dist/ node ./dist/index.js

Windows Powershell

$env:NODE_PATH="dist/"
node ./dist/index.js
Johnny Oshika
  • 45,610
  • 33
  • 151
  • 234
0

tsc cannot convert path to relative path although you config baseUrl and paths, paths is only useful when you are coding in editor to lint your code. If you want it work, you can use ts-node and tsconfig-paths module:

$ yarn add ts-node tsconfig-paths --dev

And run this script

"start": "ts-node -r tsconfig-paths/register app.ts"

Then you can get the right perform.

syfless
  • 11
  • 2