typescriptnodemonts-node

Typescript paths not working in an Express project


I am trying to use TypeScript's paths functionality so that I don't need to use relative imports any more.

Here is my tsconfig.json file:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist",
    "rootDir": ".",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "allowJs": true,
    "paths": {
      "*": ["node_modules/*", "src/*"],
      "@config/*": ["src/config/*"],
      "@controllers/*": ["src/controllers/*"],
      "@middlewares/*": ["src/middlewares/*"],
      "@models/*": ["src/models/*"],
      "@routes/*": ["src/routes/*"],
      "@types/*": ["src/types/*"],
      "@utils/*": ["src/utils/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "firebase-config.json", "webpack.config.js"]
}

Here is my package.json file:

{
  "name": "express-ts-boilerplate",
  "version": "0.1.0",
  "description": "Express Typescript Boilerplate",
  "main": "src/server.js",
  "author": "Sriram R",
  "scripts": {
    "start": "NODE_ENV=production node dist/src/app.js",
    "dev": "nodemon src/app.ts",
    "build": "tsc -p .",
    "test": "mocha --exit -r ts-node/register src/tests/*.spec.ts"
  },
  "dependencies": {
    // Dependencies here
  },
  "devDependencies": {
    // Dependencies here
  },
}

So now in one of my files, I try @config/typeConfig but I just get cannot find module error.

Maybe it's because of nodemon but it didn't work with ts-node too. How can I get this to work?


Solution

  • Note: for a working example with nodemon, skip to the second section of my answer.

    If you mean that once you compiled the files and run the application, the modules are not found, then have a look at this thread: Module path maps are not resolved in emitted code

    "paths" is designed for use with loaders that allow remapping

    Say I have this path in my tsconfig.json:

    "paths": {
          "@config/*": ["src/config/*"]
        }
    

    And I require a file using that path in a file

    import test from '@config/test';
    

    Looking into the compiled file, I end up with

    var test_1 = __importDefault(require("@config/test"));
    

    As you can see, paths have not been resolved, it's still @config/test. The same thing will happen when testing your app with nodemon and ts-node.

    In addition, you need to use a Typescript path alias resolver, like for exampletspath.

    The TypeScript compiler will be able to resolve the paths so this will compile without problems, however the JavaScript output will not be possible to execute by Node nor a Web Browser, why? the reason is simple!

    The JavaScript engine does not know anything about the compile time TypeScript configuration.

    In order to run your JavaScript code, the path aliases now needs to be made into relative paths again, here is when TSPath comes into play.


    That being said, if you want to make things work with nodemon, the following configuration will do. Beforehand, make sure you installed tsconfig-paths.

    npm i tsconfig-paths
    

    Use this to load modules whose location is specified in the paths section of tsconfig.json. Both loading at run-time and via API are supported. (...) If you require this package's tsconfig-paths/register module it will read the paths from tsconfig.json and convert node's module loading calls into to physcial file paths that node can load.

    Perfect, we will execute node with -r tsconfig-paths/register to convert paths into physical file paths and -r ts-node/register to execute ts files on the fly and nodemon will restart the app upon changes.

    In your package.json, you need to add this (modify it as needed):

        "nodemonConfig": {
              "ignore":
                [
                  "**/*.test.ts",
                  "**/*.spec.ts",
                  ".git",
                  "node_modules"
                ],
              "watch": [
                "src"
              ],
              "exec": "node -r tsconfig-paths/register -r ts-node/register ./src/server.ts",
              "ext": "ts, js"
            },
        "scripts": {
              "dev": "nodemon"
            }
    

    Note the added configuration for nodemon.

    And finally

    npm run dev
    

    And things should be running smoothly.