TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
92: Path Mapping and baseUrl
Why am I dealing with "relative path hell"?
You've probably seen it by now. You're working in a file deep inside src/features/billing/components/InvoiceList/InvoiceItem.ts and you need to import a utility from src/core/utils/dateFormatter.ts. Your import ends up looking like this:
import { formatDate } from '../../../../core/utils/dateFormatter';
It's ugly, it's fragile, and if you move that file one folder deeper, the whole thing breaks. I used to just tolerate this until I realized that TypeScript has a way to let us define "absolute" paths from a specific root, making our imports look clean regardless of where the file lives in the directory tree.
How do I actually configure these aliases in my tsconfig?
To fix this, we use two settings in the tsconfig.json: baseUrl and paths. Think of baseUrl as the starting point for all non-relative imports, and paths as a map of shortcuts (aliases).
Let's say we want to use @core/* to refer to anything inside src/core. Here is how you'd set that up:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@core/*": ["src/core/*"],
"@features/*": ["src/features/*"]
}
}
}
Now, instead of counting dots and slashes, you can just write:
import { formatDate } from '@core/utils/dateFormatter';
I personally prefer the @ prefix for aliases. It makes it immediately obvious to anyone reading the code that this is a mapped path and not a package installed from npm. It just keeps things tidy.
Why does my code compile but fail when I actually run it?
This is the "gotcha" that trips up almost everyone the first time they use path mapping. Here is the deal: TypeScript does not rewrite your import paths during compilation.
When tsc runs, it checks that @core/utils/dateFormatter exists and is type-safe, but it leaves that string exactly as it is in the resulting JavaScript. When Node.js tries to run that code, it looks for a folder named @core in node_modules, doesn't find it, and crashes with a MODULE_NOT_FOUND error.
To fix this, you need a runtime helper. If you're using Node, the tsconfig-paths package is the standard solution. You just register it at the start of your app:
import 'tsconfig-paths/register';
// Now your @core imports will actually resolve at runtime
import { formatDate } from '@core/utils/dateFormatter';
If you're using a bundler like Webpack or Vite, you'll usually handle this in the bundler's own alias configuration, but the principle is the same: TypeScript handles the type checking, and the runtime/bundler handles the actual resolution.
📋 Practical Task
Refactoring the Invoice Management Import Maze
You have a project with the following structure:
src/core/api/httpClient.tssrc/core/types/index.tssrc/features/invoices/components/InvoiceTable.ts
In InvoiceTable.ts, you currently have these messy imports:
import { HttpClient } from '../../../core/api/httpClient';
import { InvoiceType } from '../../../core/types';
Your Task:
- Modify the
tsconfig.jsonto set thebaseUrlto the project root and create a path mapping so that any import starting with@core/points to thesrc/core/directory. - Rewrite the imports in
InvoiceTable.tsto use the new alias. - Explain in a comment which package you would need to install to ensure these aliases work when running the code via
node.
There are no comments for now.