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
58: Project References for Monorepos
Why can't I just use path aliases for my monorepo?
I see this a lot. You've got a shared folder and an app folder, and you've set up paths in your tsconfig.json so you can import @my-project/shared without using a dozen relative dots. It works for the IDE, but it's a lie to the compiler.
Path aliases are just "shortcuts" for the type checker. They don't establish a real dependency relationship. If you change a type in your shared library, TypeScript doesn't inherently know that the app needs to be re-checked or rebuilt. In a massive project, you end up running tsc on the whole world every time, which is a productivity killer. Project References turn those "shortcuts" into a formal graph. It tells TypeScript: "The app depends on the shared library; don't even bother looking at the app until the shared library is valid and built."
How do I actually configure these references without it breaking?
It boils down to two main things: the composite flag and the references array. Let's say we have a core package and a web-app package.
First, in your core/tsconfig.json, you must set composite: true. This tells TypeScript that this project is intended to be a dependency for other projects and enables the generation of .d.ts files automatically.
// core/tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
}
}
Then, in your web-app/tsconfig.json, you point back to that directory. I usually suggest keeping your configs lean and using extends if you have a base config, but the reference is the critical part:
// web-app/tsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"references": [
{ "path": "../core" }
]
}
One thing I've learned the hard way: make sure your outDir is set. Since composite projects rely on build artifacts (the .d.ts files) to speed things up, TypeScript needs a consistent place to find them.
Do I still have to run tsc in every single folder?
God, no. That would be a nightmare. Once you have project references set up, you stop using the standard tsc command and start using the build mode: tsc --build (or tsc -b for short).
I love tsc -b because it's smart. It looks at the timestamps of your files and the produced .d.ts files. If you only changed a file in web-app and didn't touch core, it'll skip rebuilding core entirely. It's essentially a build system baked directly into the compiler.
You can even create a "solution" tsconfig.json at the root of your monorepo that doesn't compile any code itself, but simply lists all your projects. It looks like this:
// tsconfig.json (Root)
{
"files": [],
"references": [
{ "path": "./core" },
{ "path": "./web-app" }
]
}
Now, you just run tsc -b from the root, and TypeScript handles the entire dependency tree for you. It's a massive quality-of-life upgrade once your project grows beyond a single directory.
📋 Practical Task
Refactoring the "Auth-Service" Dependency Graph
You have a monorepo with two directories: packages/auth-lib and packages/api-server. Currently, api-server imports types from auth-lib using relative paths, and the whole project is compiled using one giant tsconfig.json at the root, which is becoming incredibly slow.
Your goal: Convert this to use Project References.
- Create a
tsconfig.jsoninsidepackages/auth-libthat enablescompositeand specifies anoutDirof./dist. - Create a
tsconfig.jsoninsidepackages/api-serverthat references theauth-libdirectory. - Create a root-level
tsconfig.json"solution" file that references both packages but compiles no files of its own. - Verify the setup by running the correct
tsccommand from the root to build the entire project incrementally.
There are no comments for now.