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!
Top comments (0)