-
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
93: Skip Lib Check and Build Performance Flags
I was working on a project the other day where I decided to pull in a couple of heavyweight dependencies—some legacy AWS SDK types and a few utility libraries. Everything seemed fine until I ran tsc to build for production. Suddenly, my terminal exploded with 40+ errors. The weird part? Not a single one of those errors was in a file I had actually written.
Wait, why is node_modules complaining?
I started scrolling through the errors, and they all looked like this: error TS2322: Type 'X' is not assignable to type 'Y', but the file paths were all buried deep inside node_modules/@types/....
Now, this is frustrating. I didn't write the types for the AWS SDK, and I certainly can't go into node_modules and start editing declaration files to fix a type mismatch between two different libraries. I just want my code to compile. I noticed that the libraries were technically compatible at runtime, but their type definitions were arguing over a shared global interface. It's a classic "type war" in the dependency tree.
// My terminal looks like this:
// node_modules/@types/node/lib/index.d.ts:123 - Error: Duplicate identifier 'Process'
// node_modules/@types/some-other-lib/index.d.ts:45 - Error: Duplicate identifier 'Process'
I tried ignoring the files using exclude in my tsconfig.json, but that didn't work. Why? Because exclude stops TS from starting the search there, but if my own code imports a library, TS follows that import right back into node_modules to check the types. It's doing exactly what it's supposed to do—being thorough—but in this case, it's being too thorough.
Silence the noise
I remembered a flag that usually handles this. I opened my tsconfig.json and added "skipLibCheck": true to the compilerOptions.
{
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"skipLibCheck": true,
"strict": true
}
}
I ran tsc again. Magic. The errors vanished.
Here is the deal: skipLibCheck tells TypeScript, "Don't type-check the .d.ts files." It still uses those files to give me intellisense and type-check my usage of the library, but it stops trying to validate that the library's own types are internally consistent or compatible with every other library in the project. Since you can't fix the types in a third-party package anyway, checking them every single time you build is mostly a waste of energy.
Taming the build time
Once the errors were gone, I noticed another problem. As the project grew, the build time started creeping up. I'm talking about those awkward 5 to 10-second pauses every time I wanted to check for errors. In a professional CI/CD pipeline, those seconds multiply into minutes.
I wondered if TS was re-checking every single file from scratch every time I changed one line of code. To test this, I looked into incremental builds. I added "incremental": true to my config.
{
"compilerOptions": {
"skipLibCheck": true,
"incremental": true
}
}
The first time I ran the build, it took the usual amount of time. But then, I changed a single string in one file and ran it again. It was nearly instantaneous. I looked in my project root and saw a new file: tsconfig.tsbuildinfo.
That file is basically TypeScript's "save game." It stores a graph of the project's files and their dependencies. Next time you run the compiler, TS looks at that file, realizes only userController.ts changed, and only re-checks that file and the things that depend on it. If you're working on a large codebase, this is non-negotiable for your sanity.
Between skipLibCheck (which stops TS from worrying about things you can't control) and incremental (which stops TS from repeating work it's already done), the developer experience goes from "sluggish and noisy" to "snappy and focused."
📋 Practical Task
Optimizing a Bloated Type-Check Cycle
You have been handed a project with a tsconfig.json that is causing slow build times and unnecessary errors originating from the node_modules folder. Your goal is to optimize the build performance and silence third-party type conflicts.
Requirements:
- Modify the
tsconfig.jsonto ensure that TypeScript does not perform type checking on declaration files (.d.ts). - Enable the feature that allows TypeScript to save information about the project graph to disk to speed up subsequent compilations.
- Ensure
strictmode remains enabled, as we don't want to sacrifice type safety in our own source code.
// Current tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"strict": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
There are no comments for now.