tsconfig.json — Interview Questions¶
20+ questions across junior, middle, senior, and professional levels. Each answer is concise but complete. Practice explaining out loud, not just recognizing the answer.
Table of Contents¶
- Junior Interview Questions
- Middle Interview Questions
- Senior Interview Questions
- Professional / Deep-Dive Questions
- Rapid-Fire Round
- Scenario Questions
Junior Interview Questions¶
Q1: What is tsconfig.json and what does it do?
It is the configuration file for the TypeScript compiler. It tells
tscwhich files to compile (include/exclude/files) and how to compile them (compilerOptions). Its presence in a directory marks that directory as a TypeScript project root, and editors, CI, and CLI all read the same file for consistent behavior.
Q2: Name the main top-level fields of tsconfig.json.
compilerOptions,include,exclude,files,extends,references,watchOptions, andtypeAcquisition. Everything exceptcompilerOptionsconfigures structure and file selection;compilerOptionsholds the actual compiler flags.
Q3: What is the difference between include and files?
includetakes glob patterns (src/**/*) that TypeScript expands to discover files.filesis an explicit list of individual file paths with no globbing.filesis rarely needed;includeis the normal approach. Also,excludefiltersincluderesults but never affectsfiles.
Q4: Does exclude guarantee a file is never compiled?
No.
excludeonly filters the initial file discovery (the root set). If an included file imports an excluded file, the excluded file is still compiled because it is reachable through the module graph.
Q5: Can tsconfig.json contain comments?
Yes. TypeScript parses it as JSONC, so
//line comments,/* */block comments, and trailing commas are all allowed. This lets you document each option inline.
Q6: What does an empty {} config do?
It is valid. TypeScript applies defaults and compiles every
.ts/.tsx/.d.tsfile under the project root (excludingnode_modulesand the output directory).
Q7: How do you create a starter tsconfig.json?
Run
tsc --init, which generates a heavily commented config with sensible defaults and explanations for common options.
Middle Interview Questions¶
Q8: How does tsc find the tsconfig.json?
With no file arguments,
tsclooks in the current directory and walks up parent directories until it finds atsconfig.json. You can point at a specific one with-p/--project. Important: passing file arguments directly (tsc src/index.ts) disables config reading entirely.
Q9: What glob wildcards are supported in include/exclude?
*(any characters except path separator),?(one character), and**/(any directory at any depth). Extension-less globs auto-expand to supported extensions (.ts,.tsx,.d.ts, plus.js/.jsxifallowJs).
Q10: Explain how extends merges configurations.
The base config is loaded first, then the child overrides it.
compilerOptionsmerge key-by-key (child wins). Arrays likelibare replaced, not concatenated.files/include/excludefrom the child fully replace the base's (if the child defines them).referencesare never inherited.
Q11: How do you reference a community base config like @tsconfig/node20?
Install it (
npm i -D @tsconfig/node20) and set"extends": "@tsconfig/node20/tsconfig.json". No./prefix — it resolves as a Node module fromnode_modules. Local files need./or../.
Q12: Why split a project into tsconfig.json and tsconfig.build.json?
So the editor type-checks everything (including tests) for a good DX, while the production build emits only
srcand excludes tests fromdist. Both extend a shared base to avoid duplication.
Q13: What does noEmit: true mean and when do you use it?
It tells
tscto type-check but produce no output files. You use it when a bundler (Vite, esbuild, webpack) handles transpilation and you only want TypeScript as a type-checker, typically in CI astsc --noEmit.
Q14: What's the relationship between rootDir and outDir?
rootDirdefines the base of your source tree;outDiris where compiled output goes. TypeScript mirrors the directory structure fromrootDirintooutDir. IfrootDirissrcand a file issrc/api/user.ts, the output isdist/api/user.js.
Senior Interview Questions¶
Q15: What are project references and why do they speed up monorepo builds?
Project references let you split a repo into independent TS projects with declared dependencies via
references. Each referenced project setscomposite: true, emits.d.ts, and writes a.tsbuildinfo. Downstream projects type-check against the cheap.d.tsdeclarations rather than re-processing upstream source.tsc -bbuilds them in dependency order and skips up-to-date projects.
Q16: What does composite: true require and imply?
It implies
declaration: trueand enables incremental.tsbuildinfooutput, marking the project as referenceable. It requires that every input file be listed byinclude/files, and typically arootDir. Stray files cause "not listed in project" errors.
Q17: Difference between tsc and tsc -b?
Plain
tsccompiles a single project and does not followreferences; it expects referenced outputs to already exist.tsc -b(build mode) understands references, topologically orders projects, performs up-to-date checks, and incrementally builds only what changed. In a references repo you must usetsc -b.
Q18: How does incremental rebuilding decide what to rebuild?
Via
.tsbuildinfo, which stores per-file content hashes and.d.tssignatures plus the import graph. A changed file is rebuilt; dependents are rebuilt only if the changed file's public.d.tssignature changed. Editing a function body without changing its type doesn't cascade. Changing compiler options or the TS version invalidates the whole cache.
Q19: What is a "solution-style" root config?
A root
tsconfig.jsonwith"files": []and onlyreferences. It compiles nothing itself; runningtsc -bat the root builds every referenced project in order. It is the idiomatic monorepo build entry point.
Q20: How should paths/baseUrl be handled in a monorepo, and what's the catch?
paths/baseUrlonly affect TypeScript's type resolution — they do not rewrite emitted import specifiers. So a matching runtime/bundler alias is required, or imports break at runtime. In references monorepos, prefer real package names via workspaces (which give correct build order and runtime resolution) overpaths.
Q21: Why might you set declarationMap: true?
It emits
.d.ts.mapfiles so that "Go to Definition" across package boundaries jumps to the original.tssource instead of the generated.d.ts. A big DX win in monorepos.
Professional / Deep-Dive Questions¶
Q22: Walk through how tsc turns a tsconfig.json on disk into a compilation.
Locate config (walk up dirs unless
-p/files given) → parse as JSONC into a JSON AST → convert to object → resolve and mergeextendsrecursively → normalize options (string enums to internal enums), apply defaults, compute implied options → expandinclude/exclude/filesinto a concretefileNameslist → build theProgram, during which module resolution may add more files reachable by imports.
Q23: What exactly is stored in .tsbuildinfo?
A serialized program state: a file-name table, per-file content hashes (
version) and.d.tssignatures, the import/reference graph (referencedMap), cached semantic diagnostics, the compiler options used, and the TypeScript version that wrote it. Option or version mismatches invalidate it entirely.
Q24: Why are comments allowed in tsconfig.json when it ends in .json?
Because TypeScript parses it with its own scanner/parser (JSONC), not
JSON.parse. The parser preserves node positions so diagnostics can point at exact lines.
Q25: How does the up-to-date check in tsc -b work?
For each project: if no
.tsbuildinfo, or the TS version differs, or options changed, or any input file is newer than the build info, or a referenced project has newer.d.tsoutput, or outputs are missing/older than inputs → rebuild. Otherwise skip.tsc -b --verboseprints the reason per project.
Q26: What's the single best command to debug "why is this file in/out of my build"?
tsc --showConfig— it prints the fully resolved config afterextendsmerging and glob expansion, including the concretefileslist. It is the authoritative view of what the compiler actually sees.
Q27: How do default excludes behave when you specify your own exclude?
The default excludes (
node_modules,bower_components,jspm_packages) apply only when you do not provide your ownexclude. If you set a customexclude, it replaces those defaults — exceptoutDir, which is always excluded. So a customexcludeshould typically re-listnode_modules.
Rapid-Fire Round¶
| Question | Answer |
|---|---|
| Field for inheritance? | extends |
| Field for monorepo deps? | references |
| Build all references? | tsc -b |
| Type-check only? | noEmit: true |
| Skip node_modules .d.ts checking? | skipLibCheck: true |
Implies declaration? | composite: true |
| Incremental cache file? | .tsbuildinfo |
Emit .d.ts? | declaration: true |
| Master strictness switch? | strict: true |
| Output folder option? | outDir |
| Show resolved config? | tsc --showConfig |
Array extends since? | TypeScript 5.0 |
paths affects runtime? | No — type-only |
| Default include if none set? | **/* |
Does exclude stop imported files? | No |
Scenario Questions¶
S1: A teammate says "I excluded legacy.ts but it's still in dist." What happened?
Some included file imports
legacy.ts.excludeonly filters root discovery, not the import graph. Either remove the import or restructure so excluded files aren't referenced.
S2: CI passes but the editor shows type errors (or vice versa). Why?
The editor and CI are likely using different configs. The editor picks the nearest
tsconfig.jsonto the open file; CI may runtsc -p tsconfig.build.json. Align them (shared base, consistentinclude), and use--showConfigto compare.
S3: After upgrading TypeScript, the first CI build is slow even with caching. Why?
.tsbuildinforecords the TS version. A version change invalidates all incremental caches, forcing a full rebuild. Bust the CI cache key on TS version bumps; subsequent runs are fast again.
S4: You added "lib": ["DOM"] to a child config and lost all your ES2022 types. Fix?
Arrays are replaced, not merged, across
extends. Restate the full array:"lib": ["ES2022", "DOM", "DOM.Iterable"].
S5: tsc reports "File is not listed within the file list of project." What's wrong?
A
compositeproject has an input file not covered byinclude/files. Composite projects must list all their files. Widenincludeto cover the file (e.g.,["src"]) or add it tofiles.
S6: You run tsc src/index.ts and none of your config's strict settings apply. Why?
Passing files directly disables
tsconfig.jsonreading entirely. Usetsc(no args) ortsc -p tsconfig.jsonso the config is honored.
S7: A monorepo build rebuilds every package on every CI run. How do you investigate?
Run
tsc -b --verboseto see the per-project rebuild reasons. Common causes:.tsbuildinfonot cached between runs, compiler options changing per run, or a TS version mismatch. Cache.tsbuildinfo+distkeyed on source + tsconfig + TS version.
S8: How would you onboard strict mode incrementally on a huge legacy repo?
Add
strict: falseplus individual flags (noImplicitAny, thenstrictNullChecks) one at a time in a base config, fixing errors per flag. Or scope strictness per-package via separate configs that extend a stricter base only where the team is ready. The goal is one base config that ratchets strictness upward over time.
Practice tip: for each answer, be ready to back it with a concrete
tsconfig.jsonsnippet or atsccommand. Interviewers value the ability to show, not just tell.
Extended Q&A — Configuration Mechanics¶
Q28: What is the difference between module and moduleResolution?
modulecontrols the module format of the emitted JavaScript (e.g.,CommonJSproducesrequire/module.exports,ESNextproducesimport/export).moduleResolutioncontrols how the compiler finds imported modules at type-check time (e.g.,Node10,Node16,NodeNext,Bundler). They are related but distinct: one is about output, the other about resolution. Modern settings often pairmodule: "NodeNext"withmoduleResolution: "NodeNext".
Q29: When would you use moduleResolution: "bundler"?
When a bundler (Vite, esbuild, webpack, Parcel) handles the actual module loading.
Bundlerresolution (TS 5.0+) mirrors how bundlers resolve imports — it allows extensionless imports andpackage.jsonexportsresolution without forcing Node's strict ESM rules. It pairs withnoEmit: truebecause the bundler, nottsc, produces output.
Q30: What does esModuleInterop actually do?
It changes how default imports of CommonJS modules are emitted and typed. Without it,
import express from "express"may fail because CommonJS modules have no real default export. WithesModuleInterop, TypeScript emits interop helpers (__importDefault) so the import behaves intuitively, and it impliesallowSyntheticDefaultImportsfor the type side.
Q31: How do typeRoots and types differ?
typeRootslists the folders scanned for declaration packages (default:./node_modules/@types).typeslists the specific packages from those roots whose global declarations are included. Settingtypes: ["node"]means only@types/nodeglobals load, even if other@types/*packages are installed.
Q32: Why might a project set forceConsistentCasingInFileNames?
To prevent bugs where an import works on case-insensitive filesystems (macOS, Windows) but breaks on case-sensitive ones (Linux/CI). With the flag on,
import "./User"andimport "./user"referring to the same file is an error. It is part ofstrictfamily hygiene in cross-platform teams. (It defaults totruein recent TypeScript versions.)
Q33: What is resolveJsonModule and how does it interact with include?
It lets you
import data from "./config.json"with the JSON typed by its contents. When enabled,.jsonfiles become valid module inputs; extension-lessincludeglobs will also match.jsonwhen appropriate. Without it, importing JSON is a type error.
Extended Q&A — Project References Depth¶
Q34: Walk me through setting up project references from scratch.
1) Create a
tsconfig.base.jsonwith shared options andcomposite: true. 2) In each package'stsconfig.json, extend the base, setoutDir/rootDir, and addreferencesto its dependencies. 3) Add a roottsconfig.jsonwith"files": []andreferencesto all packages. 4) Replacetscwithtsc -beverywhere (CI, scripts). 5) gitignore*.tsbuildinfoanddist. 6) Ensure cross-package imports go through real package names or declared references, not direct source paths.
Q35: What error tells you a composite project is missing files, and how do you fix it?
TS6307: File '...' is not listed within the file list of project. Composite projects must include every input file. Fix by using anincludeglob (["src"]) instead of a partialfileslist, or by adding the missing file tofiles.
Q36: Can two project references depend on each other?
No — circular references are forbidden and
tsc -berrors on them. The standard fix is to extract the shared pieces (usually shared types) into a third, leaf-level package that both depend on, breaking the cycle.
Q37: What does prepend do in a reference, and why is it rarely used?
{ "path": "...", "prepend": true }prepends the referenced project's output into the current project's output file. It only works withoutFile(single-file output), which is itself a legacy/AMD-era feature. Modern module-based projects don't useoutFile, soprependis effectively obsolete.
Extended Q&A — Build & Cache Internals¶
Q38: What is the version field inside .tsbuildinfo and why does it matter?
It records the TypeScript compiler version that wrote the cache. On the next build, if the running
tscversion differs, the cache is discarded and a full rebuild occurs. This is why upgrading TypeScript makes the first CI build slow, and why you should include the TS version in CI cache keys.
Q39: If you touch a source file without editing it, what happens on tsc -b?
The file's modification timestamp is newer than the build info, so the owning project is considered stale and rebuilt. However, because the file's content hash and
.d.tssignature are unchanged, dependent projects are not rebuilt. Timestamp affects the local project; signature affects downstream.
Q40: How would you debug "the whole monorepo rebuilds on every CI run"?
Run
tsc -b --verboseto see the per-project rebuild reason. Common causes: (a).tsbuildinfonot cached/restored between CI runs, (b) compiler options differing per run (e.g., an env-injected flag), (c) a TypeScript version mismatch, or (d) timestamps changing due to a fresh checkout. Fix by caching.tsbuildinfo+distkeyed on source + tsconfig + TS version.
Q41: Why does tsc --showConfig sometimes list files you didn't expect?
Because it shows the resolved file set after
extendsmerge and glob expansion. Ifincludedefaults to**/*(because neitherfilesnorincludeis set), or a glob is broader than intended, extra files appear. It does NOT show import-reachable files — those are added later during program construction, which is a separate source of "surprise" compilation.
Extended Q&A — Real-World Judgment¶
Q42: A junior sets strict: false to make errors go away before a deadline. What's your guidance?
Disabling
strictrepo-wide trades long-term safety for a short-term unblock and tends to be permanent. Better: keepstrict: trueand silence the specific failing spots with narrow, commented suppressions, or ratchet individual strict sub-flags. If strictness truly must be relaxed, scope it to one package via a config, with a tracked plan to re-enable.
Q43: How do you keep the editor and CI in perfect agreement?
Use a single shared base config (
tsconfig.base.json) that both the editor config and the CI/build config extend. Keepincludeconsistent. Run the sametsc -b/tsc --noEmitlocally as in CI. When they disagree,tsc --showConfigfrom each entry config reveals the difference.
Q44: When is a single flat tsconfig.json the right choice over project references?
For small-to-medium single-package projects (apps or libraries) where build time is already fast and there's no need to publish independent packages or parallelize builds. References add real setup and maintenance cost; only adopt them when build time, package boundaries, or independent versioning justify it.
Q45: How would you incrementally adopt project references in an existing repo?
Identify clear package boundaries, extract a shared base config, add
composite/declarationper package, declarereferences, add afiles: []solution root, switch CI totsc -b, then cache.tsbuildinfo. Break any circular dependencies by extracting shared types. Do it package-by-package, verifying builds at each step.
Mixed Difficulty Drill¶
Answer each in one sentence, then expand if asked.
- Where does
includelive? (top level) - What does
compositeimply? (declaration+ incremental) - Which command builds references? (
tsc -b) - Are arrays merged across
extends? (no — replaced) - Does
pathschange runtime resolution? (no — type-only) - What invalidates
.tsbuildinfofully? (option or TS-version change) - What does
noEmitdo? (type-check only, no output) - Does
excludestop imported files? (no) - How is
tsconfig.jsonparsed? (as JSONC) - What is a solution-style root? (
files: []+references)
Practice tip 2: many interviewers probe the "exclude doesn't stop imports" and "arrays replace, not merge" facts because they catch even experienced developers. Internalize both with a concrete example you can draw on a whiteboard.
Whiteboard Challenges¶
These ask you to produce config, not just describe it.
WB1: Write a tsconfig for a Node 20 ESM library that publishes types.
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["**/*.test.ts"]
}
Talk through:
declarationfor consumers,NodeNextfor ESM, separateoutDir, test exclusion.
WB2: Write the three configs for a base + build + test split.
// tsconfig.base.json
{ "compilerOptions": { "strict": true, "target": "ES2022", "esModuleInterop": true, "skipLibCheck": true } }
// tsconfig.json (editor)
{ "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true }, "include": ["src", "test"] }
// tsconfig.build.json
{ "extends": "./tsconfig.base.json", "compilerOptions": { "outDir": "dist", "rootDir": "src" }, "include": ["src"], "exclude": ["**/*.test.ts"] }
WB3: Write a solution root + two referenced packages.
// tsconfig.json (root)
{ "files": [], "references": [{ "path": "packages/core" }, { "path": "packages/app" }] }
// packages/core/tsconfig.json
{ "extends": "../../tsconfig.base.json", "compilerOptions": { "composite": true, "outDir": "dist", "rootDir": "src" }, "include": ["src"] }
// packages/app/tsconfig.json
{ "extends": "../../tsconfig.base.json", "compilerOptions": { "composite": true, "outDir": "dist", "rootDir": "src" }, "references": [{ "path": "../core" }], "include": ["src"] }
"Explain This Output" Questions¶
EO1: What does tsc --showConfig give you that reading the file doesn't?
The fully resolved view: merged
compilerOptionsfrom allextendslayers, defaults applied, string enums shown, and the concrete expandedfileslist. It answers "what does the compiler actually see?" after all inheritance and globbing.
EO2: tsc -b --verbose prints "Project 'core' is up to date because newest input is older than oldest output." Interpret it.
The builder compared input file timestamps to output timestamps and the
.tsbuildinfo, found nothing newer than the last build, and skippedcore. This is the normal "skip" message — it confirms incremental caching is working.
EO3: You see error TS6306: Referenced project '...' must have setting "composite": true. What's the fix?
A project listed in
referencesis not composite. Add"composite": trueto that referenced project'scompilerOptions(which also impliesdeclaration).
Anti-Pattern Spotting¶
For each snippet, state what's wrong.
| Snippet | Problem |
|---|---|
{ "compilerOptions": { "include": ["src"] } } | include misplaced inside compilerOptions |
{ "extends": "base.json" } | Missing ./ for a local file |
{ "compilerOptions": { "outDir": "src" } } | Output overlaps source |
tsc src/index.ts (expecting config) | Files on CLI bypass tsconfig.json |
tsc at a files: [] root | Need tsc -b for references |
child lib: ["DOM"] over base ["ES2022"] | Array replaced; ES2022 lost |
paths set, no runtime alias | Type-only; breaks at runtime |
Closing advice: in interviews, when unsure, narrate your debugging approach — "I'd run
tsc --showConfigto confirm the file set, then--explainFilesto see why a file is included." Demonstrating a systematic process scores as well as knowing the answer outright.
Deep Follow-Up Q&A¶
Q46: A candidate says "tsc reads tsconfig.json no matter how you call it." Correct them.
That's false. If you pass file arguments (
tsc src/a.ts),tscignorestsconfig.jsonentirely and uses only CLI flags. The config is read only when you runtscwith no files (it searches up the tree) ortsc -p <config>.
Q47: Why is composite required on referenced projects, conceptually?
Because a referenced project must publish a stable, declared type surface (
.d.ts) that dependents consume instead of its source, and it must keep incremental state so the build graph can determine staleness.compositebundles those requirements: it forcesdeclarationoutput and enables.tsbuildinfo, and it requires the project to list all its files so the graph is complete.
Q48: What's the practical difference between incremental and composite?
incrementalalone gives you.tsbuildinfo-based fast rebuilds for a single project.compositeis a superset for the references world: it impliesincremental+declarationand marks the project as referenceable by others. Useincrementalfor a standalone project; usecompositefor a project that participates inreferences.
Q49: How would you let a TypeScript app and a Jest/Vitest setup share types but build differently?
One
tsconfig.base.jsonwith shared strictness/target. The app build config extends it and emits todistexcluding tests. A test config extends it, setsnoEmitand testtypes(e.g.,["node", "vitest/globals"]), and includes bothsrcandtest. The editor uses the broad (test-inclusive) config so you get errors while writing tests.
Q50: What does verbatimModuleSyntax do and why does it matter for modern builds?
It enforces that
import/exportsyntax is emitted verbatim, requiring explicitimport typefor type-only imports and forbidding ambiguous elision. This makes per-file transpilation by esbuild/swc safe and predictable (no surprise import removal), which matters whentsconly type-checks and a bundler emits.
Q51: When debugging "module not found" in the editor only, what do you check first?
Confirm which
tsconfig.jsonthe editor uses for that file (nearest config wins), then runtsc --traceResolutionto see the resolution steps. Common causes: wrongmoduleResolution, missingtypes/typeRoots, or apathsalias the editor honors but the runtime/bundler doesn't.
Behavioral / Architecture Questions¶
Q52: Your team's CI type-check takes 4 minutes and blocks every PR. What's your plan?
Profile with
--extendedDiagnosticsto find the dominant phase. Quick wins:skipLibCheck,incrementalwith cached.tsbuildinfo. Structural wins: project references so only changed packages re-check, and offloading emit to esbuild whiletsc --noEmitis the gate. Cache keyed on source + tsconfig + TS version. Measure each change; target the biggest phase first.
Q53: How do you prevent config drift between developers?
Commit all configs (base + leaves) to the repo; never rely on per-machine flags. Pin the TypeScript version exactly in
devDependencies. Make CI run the sametsc/tsc -bcommand developers run. Use--showConfigin onboarding docs so everyone can verify the effective config.
Q54: A library you maintain needs to support both ESM and CommonJS consumers. How does tsconfig play in?
You typically build twice (or use a dual-emit tool) with two configs differing in
module/moduleResolution(NodeNextESM vsCommonJS), both emittingdeclaration. Yourpackage.jsonexportsmapsimport/requireconditions to the right outputs and types. The tsconfigs control the emitted module format;package.jsonwires the conditions.
One-Minute Summary You Can Recite¶
"
tsconfig.jsonconfigures the TypeScript compiler. Top-level fields arecompilerOptions,include,exclude,files,extends,references,watchOptions, andtypeAcquisition.tscfinds it by walking up directories unless you pass-por explicit files.include/excludeuse globs and define only the root set — imported files compile regardless ofexclude.extendsinherits config, mergingcompilerOptionskey-by-key but replacing arrays and never inheritingreferences. For monorepos, project references withcomposite: trueplustsc -bgive dependency-ordered, incremental builds cached via.tsbuildinfo."If you can deliver that paragraph cleanly and then go deep on any clause when probed, you're well-prepared for any tsconfig.json interview question.
Common Wrong Answers (and the Correct Version)¶
| Wrong answer | Correct answer |
|---|---|
"exclude removes files from the build." | It removes them from the root set only; imported files still compile. |
"extends deep-merges everything." | compilerOptions merge key-by-key; arrays and file-selection fields are replaced. |
"paths makes imports work at runtime." | paths is type-only; you need a matching runtime/bundler alias. |
"tsc always reads tsconfig.json." | Passing files on the CLI bypasses it. |
"tsc builds project references." | Only tsc -b does. |
"composite is just for incremental." | It also implies declaration and marks the project referenceable, and requires listing all files. |
"Committing .tsbuildinfo shares the cache." | It's version/path-bound; commit nothing, cache it in CI. |
"Lower target is safer." | It forces heavier downleveling and is rarely needed on modern runtimes. |
Quick Definitions to Have Ready¶
| Term | One-liner |
|---|---|
compilerOptions | The object holding all compiler flags. |
include / exclude | Globs that define the root file set (not the import closure). |
files | Explicit file list, immune to exclude. |
extends | Config inheritance; merges options, replaces arrays. |
references | Declares dependencies between TS projects for tsc -b. |
composite | Makes a project referenceable; implies declaration + incremental. |
.tsbuildinfo | Incremental cache: file hashes, .d.ts signatures, options, TS version. |
| Solution root | A files: [] config that only orchestrates references. |
--showConfig | Prints the fully resolved config and file set. |
noEmit | Type-check only, produce no output. |
Final Self-Check¶
Before an interview, confirm you can:
- List all eight top-level fields from memory.
- Explain
tscconfig discovery and the CLI-files bypass. - State the
extendsmerge rules precisely (arrays replace;referencesnot inherited). - Explain why
excludecan't stop imported files. - Set up project references + a solution root and build with
tsc -b. - Describe what
.tsbuildinfostores and what invalidates it. - Name the two highest-leverage build optimizations (
skipLibCheck, incremental/references caching). - Debug any config with
--showConfig,--explainFiles, andtsc -b --verbose.
If every box is checked, you can confidently field junior-through-professional questions on
tsconfig.json.
Bonus: Five Questions Candidates Often Miss¶
- Does adding a comment break
tsconfig.json? No fortsc(JSONC), but some strict third-party JSON parsers may choke. - Is
node_modulesalways excluded? It's excluded by default discovery, but a customexcludereplaces defaults — and anincludeglob reaching into it overrides that anyway. - What happens if
fileslists a missing file? A hard error (TS6053: File not found), unlikeincludewhich silently matches nothing. - Can
extendspoint to a package? Yes — bare specifiers resolve fromnode_modules(e.g.,@tsconfig/node20/tsconfig.json). - Does
tsc -b --cleantouch source? No — it deletes only outputs (.js,.d.ts, maps) and.tsbuildinfo, never your.tssources.
Knowing these five sets you apart, because each targets a precise behavior that surface-level familiarity misses.