tsconfig & Compiler Options
A tsconfig.json file is the configuration that tells the TypeScript compiler which files to include and how to check and build them — controlling strictness, the output JavaScript version, the module system, and where compiled files go.
Learn tsconfig & Compiler Options in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
💡 Real-World Analogy
tsconfig.json is the settings panel on a power tool . strict is the full set of safety guards — slower to get used to, but it stops you losing a finger. target is the speed dial: how modern the output runs. lib is which attachments the tool knows it has fitted. outDir / rootDir are the in-tray and out-tray that keep raw stock and finished parts separate. You configure the panel once per project, and every build afterward follows those rules — so the whole team gets the same checks and the same output.
📝 A Typical tsconfig.json (read-only)
This is JSON, not runnable code — it's the file tsc reads before compiling. Each option is annotated:
1. Strict Mode & strictNullChecks
Setting "strict": true flips on a bundle of checks. The flagship is strictNullChecks : null and undefined stop being assignable to every type and become values you must handle explicitly. That single change eliminates a huge share of "Cannot read property of undefined" crashes:
2. noImplicitAny
Also part of strict , noImplicitAny turns "I couldn't figure out this type, so I'll silently call it any " into an error. It forces you to annotate parameters the compiler can't infer, closing the door on values that quietly do the wrong thing at runtime:
3. target , module & lib
These three decide what your compiled output looks like. target is the JavaScript version emitted (modern syntax is kept; older targets down-level it). module is the output module system (ES modules vs CommonJS). lib is the set of built-in API type definitions the checker is allowed to assume exist:
🎯 Your Turn
Write the null/undefined guard that strictNullChecks would require before using a value. Fill in the blanks and match the expected output.
Common Errors (and the fix)
- ❌ "Object is possibly 'null'/'undefined'": strictNullChecks caught an unguarded access. ✅ Check first ( if (x != null) ) or use x?.prop / x ?? fallback .
- ❌ "Parameter 'x' implicitly has an 'any' type": noImplicitAny wants an annotation. ✅ Add the type, or use unknown if it's truly unknown.
- ❌ "Cannot find name 'document'": the DOM lib isn't in lib . ✅ Add "lib": ["ES2020", "DOM"] for browser code.
- ❌ "Property 'entries' does not exist on 'ObjectConstructor'": your target / lib is too old for Object.entries . ✅ Raise target or add "ES2017" to lib .
- ❌ "Module can only be default-imported using esModuleInterop": importing a CommonJS default without interop. ✅ Set "esModuleInterop": true .
Pro Tips
- 💡 Run tsc --init to generate a config with every option documented inline — a great reference.
- 💡 Keep strict on. Relax individual sub-flags only when you have a concrete reason, never the whole switch.
- 💡 In bundler setups (Vite, etc.) you often run tsc --noEmit just for type-checking and let the bundler produce the output.
- 💡 Match target to your runtime. Modern Node and evergreen browsers support ES2020+, so you rarely need to down-level to ES5.
Frequently Asked Questions
Mini-Challenge: Safe Config Reader
Write a config reader that never crashes on a missing key or a missing config — the kind of defensive code strictNullChecks pushes you toward. Follow the outline, run it, and match the example output.
🎉 Lesson Complete
- ✅ tsconfig.json configures which files compile and how they're checked and built
- ✅ "strict": true enables a family of checks, headlined by strictNullChecks
- ✅ strictNullChecks forces you to handle null / undefined before use
- ✅ noImplicitAny rejects values the compiler can't type, ending silent any
- ✅ target / module / lib set the output JS version, module system, and known APIs
- ✅ outDir / rootDir organise output; esModuleInterop smooths CommonJS imports
- ✅ Next lesson: Checkpoint — Advanced Types
Practice quiz
What is a tsconfig.json file?
- A runtime config loaded by your app
- A package manifest
- The configuration telling the compiler which files to include and how to check/build them
- A test runner config
Answer: The configuration telling the compiler which files to include and how to check/build them. tsconfig.json controls strictness, output JS version, module system, and file layout for tsc.
What does 'strict: true' do?
- Enables a whole family of strict checks at once
- Enables one check
- Disables all checks
- Only affects formatting
Answer: Enables a whole family of strict checks at once. It turns on strictNullChecks, noImplicitAny, strictFunctionTypes, and several more together.
What does strictNullChecks change?
- It removes null at runtime
- It allows null everywhere
- It converts null to 0
- null/undefined become separate types you must handle before use
Answer: null/undefined become separate types you must handle before use. null and undefined stop being assignable to every type, eliminating many undefined crashes.
What does noImplicitAny do?
- Bans the any keyword entirely
- Makes a value the compiler can't infer a type for an error
- Adds any to everything
- Only warns on functions
Answer: Makes a value the compiler can't infer a type for an error. It forces you to annotate values that would otherwise be silently any.
What does the 'target' option control?
- The JavaScript language version TypeScript emits
- The module system
- Which files compile
- The lib APIs
Answer: The JavaScript language version TypeScript emits. target sets the emitted JS version; older targets down-level modern syntax.
What does the 'module' option control?
- The JS version
- The strictness level
- The module system of the output (ESNext, CommonJS, etc.)
- The output folder
Answer: The module system of the output (ESNext, CommonJS, etc.). module decides whether import/export stay as ES modules or become require().
What does the 'lib' option control?
- The output directory
- Which built-in API type definitions the checker knows (DOM, ES2017, etc.)
- The module system
- The strict flags
Answer: Which built-in API type definitions the checker knows (DOM, ES2017, etc.). lib lists known APIs; e.g. DOM makes document/window typed, ES2017 adds Object.entries.
What do rootDir and outDir do?
- Set strictness
- Pick the target
- Control imports
- rootDir is where source begins; outDir is where compiled files go
Answer: rootDir is where source begins; outDir is where compiled files go. They mirror your source structure into the output folder when tsc emits files.
Why does esModuleInterop exist?
- To speed up compilation
- To let you write natural default imports from CommonJS modules
- To enable strict mode
- To freeze imports
Answer: To let you write natural default imports from CommonJS modules. It maps CommonJS module.exports cleanly so 'import React from "react"' works.
In a Vite/bundler setup, how is tsc often used?
- To bundle the app
- Not at all
- With --noEmit, just for type-checking while the bundler produces output
- To minify code
Answer: With --noEmit, just for type-checking while the bundler produces output. The bundler handles output; tsc --noEmit is run purely to type-check.
Continue this course
- Previous: Modules & Declaration Files
- Next: Checkpoint: Advanced Types