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