Resolving "Paths Cannot Find Module" Error in tsconfig.json

MoniqueMonique
4 min read

Relative imports can quickly clutter up your codebase, making it difficult to maintain and understand. That's where aliasing comes in handy, allowing you to simplify your import statements and keep your code clean. However, as I recently discovered, implementing aliasing can also be a pain.

The Problem

While working on a TypeScript project, I found that all my imports were prefixed with src/, which felt cumbersome and messy. To tidy things up, I turned to aliasing and updated my tsconfig.json to define paths for module aliases. At first, it worked like a charm, until I tried to start the server. 🥲

The terminal slapped me with the error Cannot find module '@/middleware/auth'. Whatever updates I made to tsconfig.json produced similar variations of the dreadful Cannot find module error.

Understanding the Cause

Chances are you’re encountering the same problem. Before we discuss the solution, let’s understand the cause.

So, why is this happening?

Why is this happening

Well, TypeScript is a superset of JavaScript so it has to be compiled into JavaScript before Node.js can execute it. During this compilation process, TypeScript understands the module aliases defined in tsconfig.json and resolves them accordingly. However, the resulting JavaScript files generated by the compiler are left in the dark—they have no knowledge of these aliases, hence the perplexing error.

What does this mean?

Consider this import statement in a TypeScript file.

import { loadUser } from '@/middleware/auth';

When this code is compiled into JavaScript, it looks something like this.

const auth_1 = require('@/middleware/auth');

The issue is JavaScript doesn't understand this import alias. So when your server tries to find the module referenced in the import statement, it comes up empty-handed and so we end up with the compiler shouting at us with Cannot find module '@/middleware/auth'.

In essence, the problem lies in the disconnect between TypeScript's understanding of module aliases and JavaScript's lack thereof. Thankfully there's a solution to bridge this gap and ensure our modules can be resolved.

Let's see how we can fix this.

The Solution

After consulting a developer friend with my error, I learned about a workaround that saved the day: module-alias. Special thanks to Bob for the helpful insights!

By leveraging module-alias, I was able to fix the disconnect between my TypeScript and JavaScript files. This package lets you ‘create aliases of directories and register custom module paths in NodeJS like a boss!’. Their words not mine, but still true.

Enough chit-chat. Here’s how you can fix this issue.

  1. Update tsconfig.json.

    Here’s a snippet from my tsconfig.json.

     {
       "compilerOptions": {
         "baseUrl": ".",
         "paths": {
           "@/*": ["src/*"]
         }
       },
       "exclude": ["node_modules"],
       "include": ["src"]
     }
    
  2. Install the Required Packages.

     npm i --save-dev module-alias
     npm i --save-dev @types/module-alias
    
  3. Configure module-alias in package.json.

     {
       "scripts": {
         "dev": "nodemon",
       },
    
       "nodemonConfig": {
         "watch": [
           "src"
         ],
         "exec": "tsc && node ./dist/main.js",
         "ext": "ts,js,json"
       },
    
       "devDependencies": {
         "@types/module-alias": "^2.0.4",
         "module-alias": "^2.2.3",
         "nodemon": "^3.0.3",
         "typescript": "^5.3.3"
       },
    
       "_moduleAliases": {
         "@": "./dist"
       }
     }
    

    The _moduleAliases property instructs the module-alias package on where to find the compiled JavaScript files. In this setup, the compiled files are located in the dist directory in the root of the server folder. The @ symbol in the imports within the dist directory will be replaced with the appropriate file path.

    Here’s what happens behind the scenes.

    In order to register an alias it modifies the internal Module._resolveFilename method so that when you use require or import it first checks whether the given string starts with one of the registered aliases, if so, it replaces the alias in the string with the target path of the alias.

  4. Import the Package

    In your main typescript file, import the package before any code.

     // main.ts or server.ts or index.ts
    
     import 'module-alias/register';
    

With this setup, your modules in your JavaScript file should be resolved thereby dismissing the Cannot Find Moduleerror. 🚀 If you need any more configurations you can consult the documentation.

Conclusion

Encountering the Cannot find module error while configuring module aliases in TypeScript can be frustrating. The root cause of this issue lies in the disconnect between TypeScript's understanding of module aliases and JavaScript's lack thereof during compilation.

However, by leveraging tools like module-alias and following a few key configuration steps, you can bridge this gap and resolve aliases in your JavaScript files.

I wouldn't have been able to fix this issue without asking for help. So don't hesitate to ask for assistance when you need it—collaboration is key to success in software development.

Happy coding!

0
Subscribe to my newsletter

Read articles from Monique directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Monique
Monique

hi! i'm Monique, a novice writer, and i write blogs for me and developers like me — beginners to coding who want to dive a little deeper and know exactly why code behaves the way it does.