Webpack alias in TypeScript declarations

Webpack alias in TypeScript declarations

If you want to skip to solution, click here.

Background

When you are writing a large project, you will face something like this.

import { Foo } from "../../../model";

One of the common solution is to use some kind of alias, so that we can use absolute path instead of relative path.

paths in tsconfig.json allows you it to compile but it does not transform it for you. For example, if you using the following tsconfig.json

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

Then, you can write with absolute path.

import { Foo } from "@/model";

However, tsc does not transform it which means it keeps the path @/model and it fails when it runs in JavaScript.

We need to using some kinds of tools like Babel or Webpack to handle this. For example, in webpack.config.js.

const Path = require("path");

module.exports = {
  resolve: {
    extensions: [".ts", ".js"],
    alias: {
      "@": Path.resolve(__dirname, "src")
    }
  }
};

Problem solved. Done?

The problem is solved if you are building a website. However, if you want to write a library, you also want to add TypeScript declarations and unit tests.

For unit tests, it usually have some kinds of functionalities to for mock a modules, you can use that to act like an alias. For example, Jest has moduleNameMapper.

{
  moduleNameMapper: {
    "^@/(.*)": "<rootDir>/src/$1"
  }
}

However, Babel does not create TypeScript declaration and tsc does not transform paths.

Solution

After some research, I finally found ttypescript and typescript-transform-paths. ttypescript expose TypeScript transforms API which allows others to write plugins to change the transform behavior.

First, you install the ttypescript and typescript-transform-paths.

npm i -D ttypescript typescript-transform-paths

Then, add the following to tsconfig.json.

{
  "compilerOptions": {
    "plugins": [
      {
        "transform": "typescript-transform-paths",
        "afterDeclarations": true
      }
    ]
  }
}

Finally, you run the following to create TypeScript declaration. Note that it is ttsc not tsc.

ttsc --emitDeclarationOnly