TypeScript monorepo best practices: Avoid import issues

Leverage TypeScript paths to avoid problems with your shared types imports

TypeScript logo, framing advice on path aliases to prevent monorepo import breakage

When you’re working with a TypeScript monorepo, for instance using the awesome Nrwl NX (whether with Angular, React, Nest, etc), you should create many small/reusable libraries and keep your applications as light as possible, simply composing the libraries as needed.

From a TypeScript point of view, those libraries are usually consumed through their public API, which are made easily importable through TypeScript path mappings such as the following:

"paths": {
  "@didowi/common-gate-api": ["libs/common-gate-api/src/index.ts"],
  "@didowi/common-utils": ["libs/common-utils/src/index.ts"],
  "@didowi/common-testing": ["libs/common-testing/src/index.ts"],
  "@didowi/web-core": ["libs/web-core/src/index.ts"],
  "@didowi/web-ui": ["libs/web-ui/src/index.ts"],
  "@didowi/web-env/": ["libs/web-core/src/lib/environments/"],
  ...
},

Thanks to those path mappings, we can easily import things from an easy to remember/easy to type name, instead of using long relative paths.

Moreover, when combined with some linting rules, you can enforce clean boundaries between your libraries/apps and ensure that elements only import/use what they should and nothing else. For instance, using Nx, you can ensure that libraries/apps only rely on the public API of what they use, and that they never directly use the internal elements that shouldn’t be accessed.

This is trivial in some mainstream platforms & programming languages, but not so much in the modern Web :)

With path mappings in place, imports from one of the libraries become as simple:

import { whatever } from '@didowi/common-api';

BTW, if you’re wondering, Didowi is the name of the product I’m currently developing. Hopefully I should be able to write more about that one soon ;-)

Path mappings are also great because they allow to easily re-organize library internals while limiting the impact on API clients.

Another advantage on the “importing” side is that you can regroup multiple imports instead of having one line per file from which you import. Neat.

So far so good, path mappings are awesome

The problem with path mappings and IDE imports in a monorepo

Unfortunately, nothing is perfect. While path mappings are really cool, they can also introduce problems.

As I’ve stated in my last article, elements within a library should never import elements that are part of the same library through the public API barrel, through the library path mapping or even through barrels (because barrels are evil, m’kay?). I’ll write an article about why I now consider TS barrel files evil another day…

Importing elements from the same library through the library’s path mapping/through a barrel will cause issues for sure, whether that is circular dependencies (which Webpack and others will yell about) or sneaky issues such as the one I wrote about in my last article.

So what can we do to avoid such mistakes?

First of all, you can easily search & identify such invalid imports by limiting the scope of your search to a library and ensuring that you can’t find any usage of that path mapping in imports. That will let you quickly fix any issue already present, but won’t prevent the issue from appearing.

IntelliJ / WebStorm configuration

While writing this article, I’ve stumbled upon this issue in the issue tracker of Jetbrains: https://youtrack.jetbrains.com/issue/WEB-44482

According to it, setting the “ Use path mappings from tsconfig.json” option under “Settings | Editor | Code Style | TypeScript, Imports” to Only files outside specified paths” should help.

This instructs IntelliJ/WebStorm to ONLY use path mappings in imports for files outside of the corresponding paths. This means that importing an element in a library from an element in the same library will be done using a relative import, which is precisely what we need for monorepo libraries. Neat!

Based on my tests, this works great with one caveat; it seems that IJ/WebStorm only considers the current file to be “inside” of the path specified for the path mapping if the file is part of the entry point (barrel) file associated with it. If not (e.g., library internal), then IJ/WebStorm will use a barrel import.

Conclusion

In this article, we’ve seen why TS path mappings are awesome for monorepos/libraries, but also that they can introduce issues when misused.

IDEs often make mistakes with imports but I can’t blame them as the JS/TS ecosystem is really complex. Thankfully, we’ve also discovered a cool little setting hidden in IntelliJ/WebStorm to make the IDE smarter for monorepo libraries imports. I imagine that the same should be possible using VSCode, but I haven’t looked into that.

That's it for today! ✨


Go Further

Typesafe checks against TypeScript interface property names at runtime
Learn how to safely use TypeScript interface property names at runtime
TypeScript Switch Case best practices
Switch statements are sometimes considered a code smell, but when they do make sense to use, you’d better make sure you don’t forget a single case. Luckily, TypeScript can help
TypeScript best practices: Interfaces and custom types vs classes
Why you should favor TypeScript interfaces and custom types over classes

Want to go deeper?

Knowii Community - Master Knowledge Management + AI | From €4.99/month
Join 400+ members mastering Knowledge Management AND AI. Community + Courses + Tools integrated. €500+ value in Knowledge Master tier.

About Sébastien

I'm Sébastien Dubois, and I'm on a mission to help knowledge workers escape information overload. After 20+ years in IT and seeing too many brilliant minds drowning in digital chaos, I've decided to help people build systems that actually work. Through the Knowii Community, my courses, products & services and my Website/Newsletter, I share practical and battle-tested systems.

I write about Knowledge Work, Personal Knowledge Management, Note-taking, Lifelong Learning, Personal Organization, Productivity, and more. I also craft lovely digital products and tools.

If you want to follow my work, then become a member and join our community.

Ready to get to the next level?

Want to level up as a developer?

Found this valuable? Share it with someone who needs it.

Join 6,000+ readers. Get practical systems for knowledge & AI. Free.

Subscribe ✨

Free: Knowledge System Checklist

A clear roadmap to building your own knowledge system. Subscribe and get it straight to your inbox.

6,000+ readers. No spam. Unsubscribe anytime.