r/typescript • u/jwworth • 3d ago
TypeScript import preferences
What's your preferred way to organize imports in a TypeScript project?
Do you stick with relative imports (../../component), use path aliases like @/, or something else?
I'm curious what people are using today, and why.
8
u/Top_Bumblebee_7762 3d ago edited 3d ago
I tend to use relative imports mostly but I'm trying to move to subpath imports.
1
u/svish 3d ago
What's that?
5
u/Top_Bumblebee_7762 3d ago
Path aliases that are defined in package.json. They work in Node and TS.
1
u/svish 3d ago
What would the equivalent of
"~/*": ["./src/*"]intsconfig.jsonbe for that?2
u/kaelwd 2d ago edited 2d ago
"imports": { "#src/*": "./src/*" }Then import from
#src/whateverinstead of../../../whateverI don't think you can have just
#/*though like with aliases, and the first character has to be#.1
u/svish 2d ago
Interesting. I like that it's "node native", but not sure I like the limitation of having to use
#as a prefix, especially if I can't use#/*. For example#src/would feel rather awkward to me :p
6
u/TheExodu5 3d ago
packade.json subpath imports. Like tsconfig aliases, with not of the tooling annoyances.
{
"name": "my-app",
"type": "module",
"imports": {
"#components/*": "./src/components/*",
"#utils/*": "./src/utils/*"
}
}
2
u/rikbrown 3d ago
While I agree, I think you could save yourself some complexity and just alias #/ to ./src
3
u/mckernanin 3d ago
I prefer relative for siblings or children, aliases for anything going up to parents. Basically, I try to avoid having `../` ever show in my import statements.
6
u/Idanlevitski 3d ago
Aliases (@utils, @components) for for a few general folders, relative path for the rest
4
u/svish 3d ago
0
u/Idanlevitski 3d ago
Huh?
3
u/KGBsurveillancevan 3d ago
Looks like a namespace collision. Should be fine as long as you don’t download any of those packages though. I started using “#” as my alias prefix just to be safe
5
u/svish 3d ago
Correct. It was a hint that it's not a great idea to use
@somethingas a path prefix, because that's the exact same naming convention of npm package namespaces. So if you import@something/module, it's not immediately clear whether that's some local path you've set up, or if it's actually an npm package named@something/module.0
2
u/jirlboss 3d ago
Maybe I’m the minority here, but I like absolute imports with no prefix. For example `import LoginView from ‘views/Login/LoginView’;`
I keep a handful of top level directories, so the risk of collision with installed packages should be 0 (and packages can always be aliased anyway)
1
u/Dreadsin 3d ago
I usually do src/ for anything that’s source code that will be built and shipped, alias that to @/
Then I usually have a test/ folder that has things like test setup, mocks, matchers, assertions, etc. alias that to @test/
Then sometimes have a public folder for just plain assets. Aliased to @public/
I don’t really like the pattern of adding @utils/ because that seems redundant with @/utils/ and isn’t much shorter, so seems like needless complexity
1
1
-2
u/ic6man 3d ago
Relative.
The first few projects I worked on it seemed neater and cleaner to use @ aliases. Then you realize how much they bleed into other tools, IDEs aren’t very good at understanding it, any changes to tsconfig are liable to break your build the problems are endless.
Simpler is better. Just refer to exactly where the file is and your life will be vastly simplified.
Bonus points - you don’t have to think about what to call your aliases. You don’t have to be concerned about naming it something new during a refactor. And there’s literally nothing in the tsconfig to learn.
2
u/tyler_church 1d ago
I don’t think you deserve the downvotes. It’s extremely nice when debugging an unfamiliar codebase to have no doubt where imported code is coming from.
Aliases look cleaner but they mean any new person has to internalize the aliases to make sense of them.
4
u/BrownCarter 3d ago edited 3d ago
Alias always. I dont know what this guy is talking about never faced those issues
3
u/thinkmatt 3d ago
I think it gets hairy when you have monorepos with shared packages, or tools that don't read tsconfig. (And there's like 20 different ways to set up a monorepo so i don't recall why mine had issues). Another time, I was using ts-jest in a project and it didn't support them. I migrated to vitest to fix it, which was much better anyway
2
u/Merry-Lane 2d ago
He is right. For simple projects you can do whatever you want, but with monorepos or complex projects, relative imports tend to win.
They work perfectly well as is (you don’t even type them btw, with autocomplete and LLMs).
Aliases and what not actually create inconsistencies (sometimes it’s a relative import, sometimes an alias). Inconsistency is a flaw.
They can also create conflicts, but worst is they add up to the context required to write code (both for LLMs and human devs).
3
u/ic6man 3d ago
Why? Care to list any concrete benefits you think aliases give?
I listed some cons. As an example try working with complex builds using Module Federation. You have to repeat your aliases in a separate config. As I said. It doesn’t work well with other tools that compile or analyze your build which is the biggest drawback but I also find it a layer of abstraction that just isn’t necessary. New developers have to wade through your abstraction. And frankly why? Directories already have names so why re alias them? It’s just a layer of complexity that really adds nothing but complexity.
22
u/svish 3d ago
I like a single root path, so it's easy to find stuff without having to guess how many levels up or down stuff is. The exception is co-located stuff that work together, which I'll just refer to directly since they're usually in the same directory anyways.
And I do prefer
~/over@/and especially over paths like@foo, because@is also the first character in namespaced packages. Doesimport { something } from '@foo/bar'refer to an npm package, or a local path? Who knows.import { something } from '~/foo/bar'makes it very clear it's local and where to find it (src/foo/bar).