We’ve all been there. You’re working on a growing project, and suddenly your import statements look like a game of directory leapfrog: import { UserService } from '../../../../services/user.service'. It’s fragile, ugly, and a nightmare to refactor.
TypeScript provides a built-in way to solve this via paths in tsconfig.json. However, there is a massive catch: TypeScript does not rewrite these paths during compilation. When you run tsc, your beautiful @services/user alias remains exactly like that in the generated JavaScript, which causes Node.js to crash because it has no idea what @services means.
That’s where tsc-alias comes in. In this guide, I’ll show you exactly how to use tsc-alias in TypeScript to bridge this gap and keep your production code as clean as your source code.
Prerequisites
- A working knowledge of TypeScript for beginners.
- An existing TypeScript project initialized with
npm initandtsc --init. - Node.js installed on your local machine.
Step 1: Configure Path Aliases in tsconfig.json
Before we can use tsc-alias, we need to tell TypeScript which aliases we want to use. Open your tsconfig.json and locate the compilerOptions section.
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@models/*": ["src/models/*"],
"@services/*": ["src/services/*"],
"@utils/*": ["src/utils/*"]
}
}
}
Note: The baseUrl is required when using paths. I usually set it to "." to ensure paths are resolved relative to the project root.
Step 2: Install tsc-alias
Since we only need this tool during the build process, we install it as a development dependency.
npm install --save-dev tsc-alias
Step 3: Update Your Build Script
This is the crucial part. Instead of just running tsc, we need to run tsc-alias immediately after the compilation is finished. This tool scans your output folder (usually dist or build) and replaces the aliases with actual relative paths that Node.js understands.
Update your package.json scripts as follows:
{
"scripts": {
"build": "tsc && tsc-alias"
}
}
Now, whenever you run npm run build, the process happens in two stages: First, TypeScript compiles the code; second, tsc-alias cleans up the paths. As shown in the image below, the final JavaScript output will no longer contain the @ symbols, but will instead use corrected relative paths.
Step 4: Testing the Implementation
To verify it’s working, create a file in src/services/api.ts and import something using an alias in src/index.ts:
// src/index.ts
import { ApiClient } from '@services/api';
const client = new ApiClient();
console.log('Connected!');
Run your build command: npm run build. Check your dist/index.js file. You should see that @services/api has been converted back to something like ./services/api.
Pro Tips for Better Project Structure
- Consistent Naming: I always use the
@prefix for aliases (e.g.,@core,@shared). This makes it immediately obvious to other developers that the import is an alias and not an npm package. - Avoid Over-Aliasing: Don’t create an alias for every single folder. Focus on top-level directories like
@components,@hooks, or@lib. - Integration with Jest: If you use Jest, remember that
tsc-aliasonly helps with the build. You’ll still needmoduleNameMapperin yourjest.config.jsto make tests pass.
Troubleshooting Common Issues
Issue: “Module not found” during runtime
If you’re still seeing errors, double-check that your outDir in tsconfig.json matches where you expect the files to be. tsc-alias looks for the compiled files; if it can’t find them, it won’t replace anything.
Issue: tsc-alias isn’t updating files
Ensure you are running tsc before tsc-alias. If you use a build tool like Webpack or Vite, you might not need tsc-alias because those tools handle path resolution during bundling. This tool is primarily for projects compiled directly to JS for Node.js execution.
What’s Next?
Now that you’ve cleaned up your imports, you might want to look at other ways to optimize your development workflow. If you’re still early in your journey, I highly recommend my migrate from javascript to typescript guide to ensure your project is set up for long-term success.
Ready to scale your app? Start implementing these patterns today and stop the relative path madness!