0

Resilient Imports with Auto-Generated TypeScript Aliases

 4 months ago
source link: https://dev.to/zachbryant/resilient-imports-with-auto-generated-typescript-aliases-59go
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

If you're not familiar, TypeScript paths allow you to assign an alias to some path in your project. This is especially useful for larger projects.

Working Harder

For example, say you're editing code in a path like components/MyComponent/src/hooks/myHook.ts, and you need to import some services code. Your services code might sit at the project root next to components/. Naively, you might use a relative path like:

import { myService } from '../../../services/my-service'

Import tools will often take care of some dirty work for you, such as updating imports when you move files. This relative path is still fragile, and lends itself to tedious work if you decide to move either the component or the service.

Working Smarter

Now to fix this, you'd add paths in your tsconfig.json like:

{
   "compilerOptions":{
      ...
      "paths":{
         "@services":[
            "services/index"
         ],
         "@services/*":[
            "services/*",
            "services/index"
         ]
      }
   },
   ...
}

which would change your fragile import into something more resilient, that won't break when moved to a new folder:

import { myService } from '@services/my-service'

And the best part is that vscode users will get free path intellisense from these aliases too. You'll be able to ctrl+. and add imports via your aliases.

Make it Even Easier with Automation

But if you're working with a large project, why not spare yourself the work of manually writing globs and copy-pasting your heart out in JSON?

I created tsconfig-paths-autogen (1kB minified, 0 dependencies), which will automatically alias all of your project folders. The big change is moving from tsconfig.json to tsconfig.js. I'll explain how this works.

Suppose this is your project directory structure:

src
├── a
├── b
│   └── c
│       └── d
└── e
    └── f

If you want to alias all of them such that you can:

  • reference the project root and src,
  • import index files (with and without an trailing /),
  • go through to subfolders after an alias

then your paths would look like:

"paths": {
  "@a": ["a/index"],
  "@a/*": ["a/*", "a/index"],
  "@d": ["b/c/d/index"],
  "@d/*": ["b/c/d/*", "b/c/d/index"],
  "@c": ["b/c/index"],
  "@c/*": ["b/c/*", "b/c/index"],
  "@b": ["b/index"],
  "@b/*": ["b/*", "b/index"],
  "@f": ["e/f/index"],
  "@f/*": ["e/f/*", "e/f/index"],
  "@e": ["e/index"],
  "@e/*": ["e/*", "e/index"],
  "@/*": ["./*"],
  "~/*": ["../*"]
}

It's tedious to get all these right and maintain them. So, your new tsconfig.js would generate the paths and export itself to json, something like:

// tsconfig.js
const { generatePaths } = require('tsconfig-paths-autogen');
const { onmyjs } = require('onmyjs'); // or any other tool to export this file to JSON.

module.exports = {
  compilerOptions: {
    ...
    paths: generatePaths(baseUrl, {
      // whatever you'd like to start your aliases with, can be empty.
      rootAlias: '@',
      // how far deep to alias from baseUrl
      maxDirectoryDepth: 2,
      // alias components folder but none under it.
      excludeAliasForSubDirectories: ['components'],
      // aliases for potentially excluded directories
      includeAliasForDirectories: {
        common: 'components/common',
      },
    }),
  }
}
// export to tsconfig.json
onmyjs(module.exports, 'tsconfig.json', true);

Integrating this into your build scripts

So now you're probably asking what to do, because your tooling needs to re-generate tsconfig.json and react to changes. Here's an example for your package.json using nodemon (more on npm):

  "scripts": { 
      "dev": "nodemon -w tsconfig.js --exec \"run-s build:tsconfig dev:parcel\"",
      "build:tsconfig": "node tsconfig.js"
      "dev:parcel": "parcel src/index.html"
  }

Note that this example uses parcel, but it is tooling-agnostic. Use it in webpack or whatever you want!

I hope this helps you have a smoother time with your imports. If you have any questions, leave a comment below. Thanks for reading!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK