tsconfig.json: The Settings That Matter
A practical guide to TS compiler options interviewers ask about: strict, noImplicitAny, lib, moduleResolution, jsx, and incremental builds.
Frontend Interview Team
February 08, 2026
What you’ll learn
- What TypeScript actually does when you run
tsc - The few
tsconfig.jsonoptions that drive most real-world behavior - A practical baseline config for modern frontend apps
- Common pitfalls (that show up in interviews and production)
30‑second interview answer
tsconfig.json is TypeScript’s compiler contract: it defines the rules for type-checking (strict, noUncheckedIndexedAccess), the environment your code targets (target, lib), and how modules/types are resolved (module, moduleResolution, types). In most teams, the biggest quality lever is turning on strict mode and then tightening it with a few safety flags.
Mental model: three buckets of settings
Think of tsconfig.json as three independent concerns:
- Type safety – how strict TypeScript is while checking your code
- Runtime/emit – what JS gets produced (or not) and what runtime you target
- Module + type resolution – how imports, path aliases, and
@types/*are found
If you’re debugging a weird TS issue, first ask: Which bucket is this in?
A solid baseline for modern frontend
This is a good starting point for Next.js / Vite / React projects. Tune from here.
{
"compilerOptions": {
"target": "ES2022",
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "react-jsx",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}Notes:
noEmit: trueis common in apps where the bundler (Next/Vite) does the build and TS is just for type-checking.moduleResolution: "Bundler"is increasingly common with modern tooling. If you hit resolution issues, try"NodeNext".
The high-impact options (what interviewers actually care about)
strict
Enables a set of checks (including strictNullChecks, noImplicitAny, etc.).
Why it matters: strict mode prevents the “looks typed but actually isn’t” failure mode.
Interview stance:
“Strict mode reduces the bug surface area and makes refactors safe. I’d rather fix types once than ship runtime bugs repeatedly.”
strictNullChecks
Forces you to model null / undefined explicitly.
Common interview gotcha:
const el = document.getElementById('root');
// strictNullChecks makes el: HTMLElement | null
el.innerHTML = 'hi'; // error: possibly nullCorrect patterns:
- guard:
if (!el) return; - or assert when you really know:
el!(but sparingly)
noImplicitAny
Stops TypeScript from silently defaulting to any.
Why it matters: any spreads and disables checking like a virus.
noUncheckedIndexedAccess
Makes indexed access safer:
const map: Record<string, number> = {};
map['x'].toFixed(2); // becomes error (value might be undefined)Tradeoff: adds some friction, but catches real production bugs.
exactOptionalPropertyTypes
Changes how optional props behave:
type A = { x?: string };
const a: A = { x: undefined }; // may error with exactOptionalPropertyTypesWhy it matters: prevents confusing “optional vs explicitly undefined” bugs.
Frontend-specific options
jsx
Common values:
react-jsx(modern React, automatic runtime)preserve(let the framework handle JSX transform)
If you use Next.js, it typically manages this.
lib
Defines the global types you have access to (DOM, ES2022, etc.).
Symptoms of wrong lib:
- TS can’t find
fetch,Promise,URL, or DOM APIs
target
Defines the JS syntax output target. In frontend apps, this is often less critical because the bundler transpiles, but it can affect:
- emitted syntax (if you do emit)
- what TS assumes exists natively
Module and type resolution (most confusing in practice)
module + moduleResolution
This pair controls how imports behave.
module: "ESNext"is common for modern bundlersmoduleResolution: "Bundler"or"NodeNext"are common choices
If you see errors around .d.ts, exports fields, or ESM/CJS mismatches, it’s usually here.
baseUrl + paths
For path aliases like @/components/Button.
Important: your bundler must also understand the alias (e.g., Next config / Vite config). TS-only aliases can type-check but fail at runtime.
types / typeRoots
Controls which global @types/* packages are included.
Use these when:
- a global type is polluting the project
- you need to force-include a type package
Performance / monorepo options
incremental
Speeds up repeated type-checks by caching build info.
In monorepos, also look at:
- project references (
composite,references) - splitting configs (a base config + per-package configs)
Common mistakes (real-world + interview)
- Turning off
strictNullChecksto “fix” errors (it hides bugs, doesn’t solve them) - Using
pathsaliases in TS but not in the bundler (runtime failures) - Relying on
skipLibCheckto hide broken local types (it should only skip external libs) - Mixing ESM/CJS settings without understanding package
type+exports
Interview questions you should be ready for
- What does
strictdo? - Why is
strictNullChecksimportant in React? - When would you use
noEmit? - What’s the difference between
moduleResolution: BundlerandNodeNext? - How do you set up path aliases safely?
Quick recap
strictis the biggest win.lib,jsx, and module resolution determine most frontend friction.- Keep aliases consistent across TS + bundler.
- Prefer fixing types over weakening checks.