typescript path alias

How to configure and resolve path alias with a Typescript Project

What is a path alias?

Path alias is a way to define an absolute path in your typescript project with a word, path or a character.

a very common syntax you will see is replacing the src directory with @

The advantage of using a path alias is that you no longer need to use a lot of directory traversals in your imports.

instead of using:

import { userRouter } from '../../routers/userRouter'

you would use:

import { userRouter } from '@/routers/userRouter'

The beauty of this solution is that you no longer need to do long path traversals to import your packages, and it doesn’t matter where your file is, as the import statement will always be the same.

I have also covered how to configure path alias for react and storybook, you can check them out here:

How to configure path alias in react

How to configure path alias in storybook

Starter Project

So let us start with a sample mock project. You can go ahead with the starter repository that we have here:


https://github.com/plusreturn/typescript_path_alias
0 forks.
3 stars.
0 open issues.

Recent commits:

we’re using yarn for this one, so let us install the dependencies.

yarn

This project does not have any functionality implemented, but it’s structured like how a typical express app will be.

if we go to src/routes/admin/usersRouter.ts , we will find an example of the situation of directory traversal that I am talking about.

import { /**/ } from '../../controllers/usersController';

and if we go to src/routes/tasksRouter.ts , we will find a more barable import statement, but the issue is that we are traversing differently due to the location of the file.

import { /**/ } from '../controllers/tasksController';

this can get really annoying during development because our path to the controllers directory is always relative to where we are. Things can quickly get out of hand the more we expand the project.

How to configure Typescript Path Alias

We can do this by adding the following lines to our tsconfig.json file.

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

make sure that you are adding the baseUrl and paths lines inside the compilerOptions property.

what our configuration here is doing is that we are defining the src dirctory to have an alias of @/

What that’s going to do for us is that we can now refer to the src directory at any directory depth in our project by using @/ . meaning, instead of the previous import statements, we can now use:

import { /**/ } from '@/controllers/usersController';

The linter will not be giving out any errors if we import our modules that way, and vscode will be able to figure out what @/ is referring to.

typescript path alias import

so we’ll change usersRouter.ts to this:

import express from 'express';

import { getUsers } from '@/controllers/usersController';

export const usersRouter = express.Router();

usersRouter.get('/', getUsers);

and tasksRouter to this:

import express from 'express';
import {
  createTask,
  deleteTask,
  getSingleTask,
  getTasks,
  updateTask,
} from '@/controllers/tasksController';

export const tasksRouter = express.Router();

tasksRouter.get('/', getTasks);
tasksRouter.get('/:taskId', getSingleTask);
tasksRouter.get('/', createTask);
tasksRouter.get('/:taskId', updateTask);
tasksRouter.get('/:taskId', deleteTask);

Now if we attempt to build our project, Typescript won’t be giving us any errors.

we can build the project with this command:

yarn build # this runs -> tsc 

This will generate our compiled project in javascript, and it will be in the dist folder.

if we attempt to run the compiled code with this command:

yarn start # this runs -> node dist/index.js

then we’ll get this error:

internal/modules/cjs/loader.js:892
  throw err;
  ^

Error: Cannot find module '@/controllers/usersController'
...

code: 'MODULE_NOT_FOUND',
...

This is happening because the tsconfig.json we have only applies to our typescript code. Node is not aware of this configuration when it comes to the compiled Javascript code. So we will need to configure it so it cna understand the path alias that we configured.

Note about the path alias resolution options

for each of the provided options, they assume that you are continuining from the “How to configure Typescript Path Alias” section, and they do not build on top of each other.

How to resolve Javascript path alias with module-alias

There are various ways to do this, but the easiest thing we can do is by using a package called module-alias. This solution is specific to resolving the path aliases found in the compiled Javascript code

Install the module-alias packge

yarn add module-alias

Configure package.json

We will need a property to our package.json, called _moduleAliases

{
	...
  "_moduleAliases": {
    "@": "dist"
  },
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  },
	...
}

in there, we will let i know what our alias configuration is, but we want it to know to resolve it for the javascript compiled code.

this means that we will have to point to dist rather than src, as our javascript code is going to import the modules from there.

also notice that we did not add the trailing / after the @ in our configuration.

Import the module in index.ts

finally we’ll import the module itself, this will be in our typescript app’s entry file, typically /src/index.ts .

We will add this line at the top of the file, before any import.

import 'module-alias/register'; // 👈 add this one
import express from 'express';
import { valdiateAdminRole } from './middlewares/validateAdminRole';
import { validateToken } from './middlewares/validateToken';
import { usersRouter } from './routes/admin/usersRouter';
import { tasksRouter } from './routes/tasksRouter';

const app = express();

app.use('/api/tasks', validateToken, tasksRouter);
app.use('/api/admin/users', validateToken, valdiateAdminRole, usersRouter);

app.listen(5000, () => {
  console.log('server started at port 5000!');
});

Build the project again

yarn build # this runs -> tsc

Start the Javascript Project

yarn start # this runs -> node dist/index.js

The project will start without issues here!

running javascript code  with path alias

How to resolve a Typescript path alias with ts-node

With ts-node, the same steps we did for node configuration that are in the previous section will work as well.

Tough there is another way to do it, which is through the package tsconfig-paths.

Once again, if we attempt to run the app with ts-node, we will get this error:

$ ts-node src/index.ts
Error: Cannot find module '@/controllers/usersController'
Require stack:

so to configure the resolution part, we will:

Install the tsconfig-paths module

yarn add -D ts-node tsconfig-paths 

run ts-node with tsconfig-paths

if you have ts-node installed globally:

ts-node -r tsconfig-paths/register src/index.ts

if you have ts-node installed for just this project:

yarn ts-node -r tsconfig-paths/register src/index.ts

so the important part is we register the resolver package through the argument -r tsconfig-paths/register

for neat-ness sake, we can add this as a script in our package.json file like so:

"version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "ts-node -r tsconfig-paths/register src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  },
...

and then we can just run our code like so:

yarn dev

How to resolve path alias with ts-node and Nodemon

This approach might be the most comfortable one to have during development. It resolves the path alias, runs the code, and reloads it when the code is updated.

Install the packages

yarn add -D nodemon ts-node tsconfig-paths

configure nodemon

At the root directory of our repository, we’ll create a nodemon.json file.

in that file, we’ll add the following:

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

ignore is set to ignore the files we do not want to watch. this would be the unit testing files, git and node moduels.

watch mark our src directory as the one to watch for any of its changes. If a change is detected there, then it will re-run the exec command.

exec contains the command that we want to run whenever a file that we are tracking in watch changes.

ext filters the type of files we want to watch for.

add a script for development

in package.json, we’ll simply add this line under scripts:

....
"license": "MIT",
  "scripts": {
    "dev": "nodemon",
    ....
  },
....

run the code

yarn dev

and from this point on, our code runs while it understands the path alias, and it reloads when we update our code!

Sing up to get an email notification when new content is published

Subscription Form

2 Comments

  1. Great job, this was exactly what I was looking for with using the `module-alias` part of your blog. Appreciate it!

Leave a Reply

Your email address will not be published. Required fields are marked *

Similar Posts