In this article, I will explain how you can use typescript aliases with Babel or TSPath. If you have been using TypeScript/JavaScript (TS/JS) and have a nested folder structure, you may well be used to seeing imports like so (using es6 style imports). This is sometimes referred to as path hell and is a very common occurrence as your project grows in size.

import moduleA from "../../../moduleA";
import moduleB from "../moduleB";

These are called relative imports, as we are importing modules using paths relative to our current module/file. As you can see, they can sometimes be very ugly and hard to work out where the module is we are importing. So sometimes you will use the wrong number of “../” etc. There are a few tools we can use to help solve our problem.

Structure

In the examples below let’s assume we have a structure which looks something like this.

├── app.json
├── babel.config.js
├── App.tsx
├── README.md
├── src
│   ├── actions
│   ├── assets
│   ├── components
│   │   ├── AppHeader
│   │   │   ├── AppHeader.tsx
│   │   │   ├── index.ts
│   │   │   └── styles.tsx
│   │   ├── Logo
│   │   │   ├── index.ts
│   │   │   ├── Logo.tsx
│   │   │   └── styles.tsx
│   │   └── PhotoAlbumList
│   │       ├── index.ts
│   │       ├── PhotoAlbumList.tsx
│   │       └── styles.tsx
│   └── views
│       ├── AboutUs.tsx
│       ├── FAQ.tsx
│       ├── Home.tsx
│       └── Settings.tsx
├── tsconfig.json
├── tslint.json
└── yarn.lock

TypeScript Aliases

In TS there is an option we can set in our config file tsconfig.json, referred to as TS aliases. Let’s take a look at an example to see what it can do. Let’s say we’re in the Home.tsx file and we want to import Logo at the moment we would do something like (in this case index.ts, exports the Logo hence we don’t have to go ../components/Logo/Logo.)

// without TS aliases
import Logo from "../components/Logo";

// with TS aliases
import Logo from "~/components/Logo";

Anytime we use the ~ character in our imports it automatically starts importing from the src folder. I think this makes our imports far easier to follow and read. You can also change the TS aliases so you can have one for the components folder like @components or actions like @actions. It’s all up to you how you want to structure your project.

tsconfig.json

Now I’ve shown you what TS aliases are, but how do we add them to our project? Simple, open your tsconfig.json file and add the following two options

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

The baseUrl means we use the root directory (the directory where tsconfig.json is), and look for the src folder in the same directory.

Babel Module Resolver

Now if you start to use ~ in your imports, you shouldn’t see TS raise any issues/problems. However, if you transpile TS into JS, you’ll notice you still have ~ in your imports. Our imports do not automatically get changed. Hence earlier I suggested you could use the Babel module resolver.

One tool that works very well is the Babel module resolver. However, you need to be using Babel, Babel is a tool which is used to transpile “new JS” into plain old ES5 JS.

I will assume you already have Babel setup. If you’re using say React Native and you created the project by using the cli tool, Babel already comes configured. What you’ll need to do from there is install the plugin.

yarn add --dev babel-plugin-module-resolver
# or
npm install --save-dev babel-plugin-module-resolver

Then add the following to your Babel configuration file, which will either be something like .babelrc, babel.config.js or .babelrc.js. You can also place your configuration in the package.json file using the babel key.

If your configuration file is a JS file (ends in .js)

module.exports = {
  ...
  plugins: [
    [
      "module-resolver",
      {
        alias: {
          "~": "./src"
        }
      }
    ]
  ]
};

If your configuration file is a JSON file.

{
  // ...
  "plugins": [
    [
      "module-resolver",
      {
        "alias": {
          "~": "./src"
        }
      }
    ]
  ]
}

The module resolver will now automatically be run every time Babel is run. If you’re using React Native, this is already done for us.

TSPath

We cannot always include Babel in our projects, in this case I recommend using TSPath. For example, I had issues getting Babel to work with my Firebase Cloud Functions project, so I ended up using TSPath for that.

We use TSPath to solve the same issue as Babel module resolver, when TS -> (transpiled) to JS, JS won’t be able to resolve the import paths. First, let’s install TSPath.

yarn add --dev tspath
# or
npm install --save-dev tspath

Then we run yarn run tspath, then our path aliases become relative paths again. If your TS gets transpiled say because it’s a package being published to NPM, you can add as part of your build process, for example in my package.json I have the following

{
    "scripts": {
        // ...
        "build": "tsc -p . && npm run fix-paths",
        "fix-paths": "tspath -f"
    }
}

That’s it! We have now used TS path aliases with our project. I have shown how you can solve the path hell issue in our TS project.

Jest

If you have tests written in Jest you can also have paths like the above resolve. First you need to edit your jest.config.js file (or equivalent configuration file). Then add the following below (to have the same paths as above).

module.exports = {
  moduleNameMapper: {
    '~/(.*)': '<rootDir>/src/$1',
  },
....
};

Then in our tests we can do the following, to import our dependencies

import AboutList from "~/components/AboutList";
import { about } from "~/data";

Appendix