Path Aliases with TypeScript in React
Tags: typescript, react, alias, path
Published: 2022-12-29 17:35:11

Have you ever found yourself typing so many ../../../ in typescript files? The deeper your project is the more typing is required to access modules in higher layers.

**Paths Aliases** to the rescue! They make the code look much cleaner. We can go from ugly to fancy paths/imports like so by declaring aliases that map to a certain absolute path in your application.

```jsx
// Ugly path 😭
import { Button } from './../../../components/Button.tsx';

// Fancy path 🤩
import { Button } from '@components/Button.tsx';
```

## Setup
Imagine we have the following project structure:

```console
src
├── rest
│   ├── complex-components
│   │   └── auth
│   │       └── UserAuth.tsx
│   └── components
│       ├── Button.tsx
│       └── User.tsx
├── services
│   └── user-data-service.ts
└── setupTests.js
```
### Step 1: Install TS
Let's first install typescript in our project as a root dependency.
```console 
yarn add -D typescript
```

### Step 2: Configure Path Mappings
Next step is to create or update the `tsconfig.json` file with the path mapping. The TypeScript compiler supports the declaration of path mappings using _paths_ property in `tsconfig.json` files.

```json
{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "@...": ["..."] // This mapping is relative to "baseUrl"
    }
  }
}
``` 

> ☝️ Please notice that _paths_ are resolved relative to _baseUrl_. When setting _baseUrl_ to a value other than ".", i.e. the directory of `tsconfig.json`, the mappings must be changed accordingly.

We are going to build our paths in a new file `tsconfig.extend.json` to make them more readable when importing and extend it from the `tsconfig.json` file. So,

- Create the `tsconfig.extend.json` file at your project’s root.

- Establish our _baseUrl_ to be used as a reference.
```json
{
  "compilerOptions": {
    "baseUrl": "./src"
  }
}
```

- Define your paths mapping. We want typescript to import all files located in _paths-to-search-for_ as _@new-path-alias_.
```json
{
  "compilerOptions": {
    "baseUrl": ".", 
    "paths": {
      "@new-path-alias": ["paths-to-search-for"] 
    }
  }
}
``` 

Like so:
```json
{
    "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
            "@components/*": [
                "./rest/components/*"
            ],
            "@complex-components/*": [
                "./rest/complex-components/*"
            ],
            "@services/*": [
                "./services/*"
            ]
        }
    }
}
``` 

- Extend the `tsconfig.extend.json` file from the `tsconfig.json` file:
```json
{
  "compilerOptions": {
    ...
  },
  "extends": "./tsconfig.extend.json",
  "exclude": ["node_modules", "**/*.test.*", "build/**/*"]
}
,
```

Now, you can use the new path aliases for module imports in your application. Let's run our code! 🎉

As soon as we run the code we get an error. What is going on here...? 🤔
```console
Error: Can't resolve import '@complex-components/auth/UserAuth'
```
A project created with `create-react-app` includes, in addition to React, other libraries like:

- **Webpack** which is in charge of processing and packaging our JavaScript code (with its dependencies), CSS files and other static files such as images and fonts.
- **Babel** which allows us to use new features of ECMAScript and JSX (JavaScript XML).
- **PostCSS** which is a library for CSS processing.
- **Jest** which is a library for testing.

**Webpack** it's also used to resolve imports so what we need to do is to modify that webpack configuration for it to resolve the alias we just defined in our `tsconfig.json`. There are multiple ways to achieve that, like:
- Running `npm run eject` command: it's used to release all the tools behind Create React App, so that we can customize how it works. All the libraries mentioned remain hidden and cannot be customized. If your project needs a more specific configuration of any of these tools, you can launch the "eject" command, which brings all the hidden Create React App configuration out. Just keep in mind that the "eject" action cannot be undone!
- Overriding Create React App configuration with [CRACO](https://www.npmjs.com/package/@craco/craco
). This is the most convenient way because we can override `create-react-app` configurations without the need to eject.

### Step 3:  Resolving alias with CRACO
First, we need to install CRACO 
```console
npm install --save @craco/craco
&&
npm install --save-dev craco-alias
```

Then, create `craco.config.js` file at your project’s root (not inside src/) with the following configuration

```js
const CracoAlias = require("craco-alias");

module.exports = {
    plugins: [
        {
            plugin: CracoAlias,
            options: {
                source: "tsconfig",
                // baseUrl SHOULD be specified
                // plugin does not take it from tsconfig
                baseUrl: "./src",
                // tsConfigPath should point to the file where "baseUrl" and "paths" are specified
                tsConfigPath: "./tsconfig.extend.json"
            }
        }
    ]
};
```
The last thing is that we won’t be using react-scripts to start up our app anymore. We have to switch from react-scripts to craco to each of start, build, test and eject in `package.json` file. 
```json
"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "craco eject"
  }
```

## Start React App with CRACO
Let's run our app
```console
yarn start
``` 
This time, our react app will not be throwing any errors for not being able to resolve our path alias!