Fixing absolute imports in TypeScript

Tom McKenzie
2 min readOct 4, 2018

--

TypeScript has a cute feature with a small problem causing a huge impact.

Cute Feature: absolute imports with baseUrl

{
"compilerOptions": {
"baseUrl": "src"
}
}

Now you can import all files relative to baseUrl, instead of to the file you’re in! In larger projects this saves you a lot of import from '../../views' annoyance — you can write import from 'views' instead, anywhere in your project.

Unfortunately, these imports won’t work in Javascript any more. And the TypeScript team are opposed to the compiler converting these import paths to relative when it does compile to Javascript, since:

Module names are considered resource identifiers and are not changed by the compiler.

So now your module can’t be imported into other modules

Most build toolchains will have some support but with tradeoffs. So, there are some… workarounds

  • Compile your TypeScript with @babel/typescript instead. Drawback: No .d.ts definition files, and no type safety at compile time.
  • In a TypeScript parent project, set up your parent (importing) project to resolve imports relative to node_modules/myModule/lib using tsconfig-paths/register, or manually “absolute to project-root relative” mappings in your parent’s tsconfig.json. Very manual.
  • Build your library with rollup-plugin-typescript2, which produces a single file with no imports, but no typing definition files.
  • Build using webpack and awesome-typescript-loader, with the right plugin configuration.
  • And others tsc hacks involving symlinks or post-build scripts

Best solution: Use the compiler itself to compile these imports to Javascript

The TypeScript compiler supports an API much like Babel’s, which allows us to transform the code it outputs. Transformers can basically turn any code into any other valid code, so we use it to re-write easy-to-write but hard-to-compile absolute imports into relative imports that node and Javascript expect. ttypescript makes it easy to compile or run with transformer plugins!

✨✨✨

✨✨✨

It’s as easy as adding the plugin to your tsconfig.json and running ttsc or tts-node as you’d usually run their native typescript equivalents.

We’ve been using it at ActivePipe (my excellent workplace) in production and are enjoying the advantage of absolute import paths when refactoring and breaking apart our larger modules while still maintaining flexibility of compiling to basically any project.

Contributions and bug reports welcome and hopefully this eases some pain the TypeScript community has been having writing modular and packaged code.

--

--