Skip to Content

Path aliases

ttsc is the standalone TypeScript-Go compiler โ€” see Setup if you havenโ€™t installed it yet.

If your tsconfig.json uses compilerOptions.paths (e.g. @/* โ†’ ./src/*), the emit will contain those alias paths verbatim โ€” and they wonโ€™t resolve at runtime. @ttsc/paths rewrites them into relative imports during compile.

The problem

// src/main.ts (source) import { value } from "@lib/value";

After a plain ttsc build:

// dist/main.js (broken) import { value } from "@lib/value"; // โ† Node can't resolve this

The fix

npm install -D @ttsc/paths
// tsconfig.json { "compilerOptions": { "rootDir": "src", "outDir": "dist", "paths": { "@/*": ["./src/*"], "@lib/*": ["./src/modules/*"] }, "plugins": [{ "transform": "@ttsc/paths" }] } }

Now the emit is:

// dist/main.js import { value } from "./modules/value.js";

What it does and doesnโ€™t do

  • Reads the same compilerOptions.paths, rootDir, and outDir that ttsc already uses.
  • Rewrites both .js emit and .d.ts declarations.
  • No separate plugin config.
  • Does not add .js extensions where they werenโ€™t already implied โ€” Nodeโ€™s NodeNext / Bundler resolution should match your module setting.
  • Does not change source files โ€” only the emit.

Common alternatives (and why this is simpler)

  • tsc-alias โ€” post-build script. Works, but itโ€™s a second pass. @ttsc/paths runs inside the compile.
  • Path remapping at runtime via tsconfig-paths/register โ€” adds runtime cost and a startup step. @ttsc/paths produces normal relative imports.
  • Custom bundler config โ€” if a bundler owns your build, thatโ€™s fine. @ttsc/paths is for projects that ship as plain Node packages.

Inspired by

typescript-transform-paths. Same goal, different compiler.

See also

Last updated on