Make Cleaner TypeScript Import Statements with Path Aliasing

Dom Segundo
5 min readJan 11, 2024

Import statements are outright the first pieces of code we get to see upon opening a file. Apart from the logic that using code before importing results to a runtime error, having these statements at the top helps us gain context of the code fragments a particular file is using.

In software engineering, the best codebases are the ones that are readable. It is essential that a developer writes software that is concise and clean. Efforts in making code more readable presents benefits in the long run. It results to less refactoring and faster turnaround times from maintenance.

Using path aliases

We could use path aliasing to make import statements much cleaner. This is most useful if code has to be imported to deeply nested components. Instead of repetitive “backtrack to parent” pattern (../) followed by the file path itself, we follow a format of having a prefix, followed by the file path. The comparison is shown below.

Without path aliasing:

//on HomeContainer.tsx
import { DictionaryService } from '../../application/services/dictionary.service';
import { ButtonPanel } from '../components/ButtonPanel';

With path aliasing; note that @application/ and @presentation/ are aliases while services/random.service and container/random-container the paths relative to the alias:

//on HomeContainer.tsx
import { DictionaryService } from '@application/services/dictionary.service';
import { ButtonPanel } from '@presentation/components/ButtonPanel';

TIP: In choosing an alias, consider naming it after a parent directory that is able to group components through separation of concerns (i.e. Model/View/Controller); or in clean architecture, by their layer. This also helps easily locate the necessary file through the directory tree even without the control/command + click shortcut.

NOTE: The usage of the @ character, just like in our example depends on your desired naming convention. It is absolutely fine to instead pick application as opposed to @application. Again, the naming convention depends on the team or organization’s standards.

Our sample project directory

Below is a project directory that we will be using. Say we are building a very simple dictionary application. For simplicity we‘ll just have 3 core components: (1) dictionary.service.ts where we assume our use cases are contained; (2) HomeContainer.tsx which then houses our ButtonPanel component, imported from (3) ButtonPanel.tsx. Then of course, we have our tsconfig.json and babel.config.js files which are usually auto-generated.

Agenda

Type aliasing requires us to modify the following files in our project, located at the root folder:

  1. tsconfig.json — this is file contains specifications on how we want our transcompiler to behave.
  2. babel.config.js (if applicable; typically for React projects) — some libraries use Babel in order to transcompile code that use complex, syntactic sugar- to code that most browsers can understand. Take React for instance, Babel converts tsx or jsx to plain javascript code.

Let’s apply the changes

First, we locate our tsconfig.json file. As said earlier, it is typically on the root folder. The json file should look something like the snippet below. The property we will be modifying is the compilerOptions prop.

{
"compilerOptions": {
"target": "es5",
},
}

We will add two nested properties or subproperties to compilerOptions.

First is the baseUrl property, which accepts a string value of our intended baseUrl directory. Typically we set it to ‘.’, which denotes the current directory where the tsconfig.json file is located, which is the root or the MyProject folder. The key thing here is that we set the baseUrl to the root so that we can access its subdirectories such as our src folder.

Next, we will add the paths property, which accepts an object with a key-value pair of the alias itself and array of relative paths to the baseUrl, where the alias points to. The key is a string, for instance “@application/*”. This denotes the alias. The value, for instance [“src/application”] is an array which contains the path that will be pointed to when we prefix with the alias @application.

  1. On tsconfig.json
{
"compilerOptions": {
"target": "es5",
"baseUrl": ".",
"paths": {
"@application/*": ["src/application/*"],
"@presentation/*": ["src/presentation/*"],
}
},
"extends": "expo/tsconfig.base"
}

Wildcards — the asterisks you see are called wildcards. The asterisk wildcards are denoted as any sequence of characters. Let’s take “@application/*”: [“src/application/*”] as an example. “@application/*” means that our compiler is looking for import statements that has the sequence of characters: @application/ followed by ANY other sequence of characters, sort of like a prefix. The asterisk wildcard then plays a slightly differnt role with [“src/application/*”]. The asterisk wildcard this time denotes that whichever directory is nested. For instance our presentation folder has subdirectories such as components and containers, the asterisk wildcard tells our compiler to include these subdirectories and their content to the scope.

2. On babel.config.js (If applicable)

If you are working on projects that utilize libraries that use complex javscript syntax or syntactic sugar such as React, Babel will be part of your dependencies. If so, a babel.config.js will be generated on your root directory.

We will have to make changes to this file in order to allow import aliasing. It should initially look something like the code snippet below:

module.exports = function (api) {
api.cache(true);
return {
presets: [],
plugins: [],
};
};

We will be modifying the plugins property by adding two arguments. But first we have to install a library called babel-plugin-module-resolver. We do this by running the command npm i babel-plugin-module-resolver on our terminal. Once installed we can now make the changes to the babel.config.js file.

First we append the string “module-resolver”. Then, we append an object with the property alias. Similar to the changes we made in our tsconfig file, we add a key value pair, but this time instead of an array of values, the property only accepts a string.

module.exports = function (api) {
api.cache(true);
return {
presets: [],
plugins: [
[
"module-resolver",
{
alias: {
'@application': "./src/application",
'@presentation': "./src/presentation",
},
},
],
],
};
};

Also, it is worth noting that we won’t be using wildcards unlike changes in the tsconfig.json file. For the alias we just specify our intended alias name or prefix. As for the path, we just specify the directory relative to our root that we intend our alias to be pointing to. This will automatically include all subdirectories and content, without the use of the asterisk wildcard.

With that, we can now replace our import statements with aliased ones, just like the ones I have shown earlier. Kindly refer to the “Using Path Aliases” section.

--

--