How to get rid of relative import in Typescript + Node 18?

260 views Asked by At

I'm currently running a backend application in Typescript 5.2, which can be build to run as a Debian service on production with node 18 or directly run in a Docker container through nodemon.

Everywhere in my app you can find relative imports, sometimes short, sometimes very long:

import { Shortcut } from './Shortcut';
import { formatQuery } from '../../../../../../helpers/formatQuery';

And I would like to get rid of these imports.


My package.json looks like:

{
  "name": "toto",
  "version": "1.4.9",
  "description": "tata",
  "scripts": {
    "start:dev": "nodemon --config ./node_modules/@titi/config/nodemon.json",
    "build": "tsc -p tsconfig.json"
  },
  "devDependencies": {
    "jest": "29.6.1",
    "ts-jest": "29.1.0",
    "typescript": "5.2.2"
  },
  "dependencies": {
    "nodemon": "3.0.1",
  },
  "volta": {
    "node": "18.17.0",
    "yarn": "1.22.17"
  }
}

I use a custom nodemon.json which is:

{
  "watch": ["src/", "*.json"],
  "ignore": [
    ".git",
    "public",
    "node_modules/**/node_modules",
    "src/**/*.test.*",
    "src/**/*.spec.*"
  ],
  "ext": "ts,js,json",
  "exec": "tsc -p tsconfig.json --incremental && node --trace-warnings --trace-uncaught build || exit 1"
}

and finally, my tsconfig.json file look like this:

{
  "compilerOptions": {
    "lib": ["es2020"],
    "module": "commonjs",
    "target": "es2020",
    "moduleResolution": "node",
    "outDir": "../../../build",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "declaration": true,
    "sourceMap": true,
    "pretty": true,
    "baseUrl": ".",
    "paths": {
      "@services/*": [
        "src/services/*"
      ],
      "@routes/*": [
        "src/routes/*"
      ],
      "@helpers/*": [
        "src/helpers/*"
      ]
    },
  },
  "files": ["../../../src/index.ts"],
}

I already know a lot of solutions do exists, I already tried a lot of them but none worked, like:

  • Setting the baseUrl property

No matter if I set it to . or ./src in my tsconfig.json, I can't start my project and I keep getting this error:

import { formatQuery } from 'src/helpers';

src/services/toto.ts:5:29 - error TS2307: Cannot find module 'src/helpers' or its corresponding type declarations.

  • Adding paths properties to the tsconfig.json

Whenever I set a paths property, it's being ignored. For example:

"@helpers/*": [ "src/helpers/*" ]

and import a function like this result to:

import { formatQuery } from '@helpers';

src/services/toto.ts:5:29 - error TS2307: Cannot find module '@helpers' or its corresponding type declarations.

(and that even if I put @helpers/formatQuery, and even with a BaseUrl to .)

  • using the NODE_PATH env variable

Since BaseUrl and paths properties change the compiled files (javascript), you can't expect JavaScript to know where is the BaseUrl, so I tried to use this env variable in my nodemon.json's "exec" like:

"exec": "tsc -p tsconfig.json --incremental && NODE_PATH=src node --trace-warnings --trace-uncaught build || exit 1"
  • Changing my IDE settings (for both JS and TS)

I'm currently using VSCode, and I read somewhere that I needed to edit the typescript.preferences.importModuleSpecifier variable to non-relative (even if I'm not fond of this way, because it globally means new developers that will works on my project won't be abble to make it works if they don't set this property in their VSCode ?)

I tried to add a jsconfig.json file with baseUrl and paths property as put in the tsconfig.json, but it didn't worked.


EDIT: I just forget to tell that the file src/helpers/index.ts do exists, and for clarity sake I'm adding here it's content:

export * from './parseString';
export * from './formatQuery';
...

So what am I doing wrong ? is my architecture too complex to use non-relative imports ?

1

There are 1 answers

8
NoNam4 On

You need to do some little changes on baseUrl and paths:

"baseUrl": "./src",
"paths": {
  "@helpers/*": [ "./very/long/path/to/helpers/*" ],
}

...

import XXX from '@helpers/XXX'

On paths you don't need to add src because baseUrl is making paths to start from this folder. If you add it then ts will search for /src/src/.... and this is not the case. Read more here.

After setting paths you need to use tsc-alias so @helpers/XXX will be correctly converted to the actual path of file after typescript build. See this for more details.