tsconfig and strict mode
What strict turns on, what each flag catches, and target versus lib.
tsconfig.json is where a project sets its standards: how strict the
checker is, what language level to emit, and which built-in libraries
exist. Interviewers ask "what does strict actually enable?" — strict: true is one switch arming a family of flags.
Real-life situation
A legacy project turns strict on and fifty errors appear overnight.
Almost all are real: an undefined passed as a name, a variable read
before assignment, a null nobody admitted. Each flag below catches one
species — strictNullChecks (no silent undefined), noImplicitAny
(no silent any), strictPropertyInitialization and friends. Turning
them on file by file beats turning them off forever.
Visual
Try it
Checked with TypeScript 6.0.3, strict mode
function initials(first: string, last: string): string {
return first[0] + last[0];
}
const pair = ["sam", "jo"];
console.log(initials(pair[0], pair[1]));
function greet(user: { name: string }): string {
return "hi " + user.name;
}
console.log(greet({ name: "sam" }));
function pickName(known: boolean): string | undefined {
return known ? "jo" : undefined;
}
const maybe = pickName(true);
console.log(greet({ name: maybe }));
function firstTag(tags: string[]): string {
const found = tags.find(function (t) {
return t.length > 0;
});
return (found ?? "none").toUpperCase();
}
console.log(firstTag(["a", "bb"]));
console.log(firstTag([]));Compiler not loaded. Check or Run loads it here (about 9 MB) — nothing downloads until you do.
Try changing pickName's return type to string — the Types tab goes
quiet, because the checker trusts the new promise. target
chooses the emitted language level (this topic uses ES2022) while lib
chooses which built-ins exist (DOM for browsers). Our sandbox pins
both, and every runner labels its mode.
With strict off, the same code compiles silently — the override below is displayed, exactly as the page promises:
Checked with TypeScript 6.0.3, strict off
const maybe: string | undefined = ["sam", "jo"][2];
function greet(user: { name: string }): string {
return "hi " + user.name;
}
console.log(greet({ name: maybe }));Compiler not loaded. Check or Run loads it here (about 9 MB) — nothing downloads until you do.
Runner helpers
sleep(ms) waits; fakeFetch(url) returns deterministic sample data. TypeScript is stripped before running; imports and exports are not available in the sandbox.
Will it compile? — decide first, then reveal.
const name: string = undefined;
console.log(name);Interview questions
What is tsconfig.json?basic
The configuration file for the TypeScript compiler: it selects which files to include, how strictly to check them, what language level to emit, and where output goes. Editors read it too, so its settings drive the errors you see inline. Every project should commit one at the root rather than relying on command-line flags. Start from the strict: true preset and relax individual options only with a written reason.
Interviewers also ask
- What are the most important options to set first?
What does strict mode enable?intermediate
Setting strict: true turns on the whole family of correctness flags at once — strictNullChecks, noImplicitAny, strict function and property checks among them. It is the recommended default because each flag closes a specific hole where errors slip through silently. Enable it for new projects without hesitation. For older codebases, turn flags on one at a time so each batch of errors stays reviewable.
Interviewers also ask
- How would you adopt strict in an old codebase?
What does strictNullChecks do?intermediate
It removes null and undefined from every type unless you list them explicitly, so string truly means a present string. Reading a possibly missing value becomes an error until you guard, default, or narrow it. That one flag eliminates the most common production crash — dereferencing nothing. Keep it on and model absence honestly with | null or optional properties instead of letting every type secretly include emptiness.
Interviewers also ask
- How do you model a value that can be missing?
What does noImplicitAny do?intermediate
It forbids the compiler from silently falling back to any where it cannot infer a type — untyped parameters and bare [] initializers become errors instead. Each error asks you to state your intent explicitly. That keeps any visible and deliberate rather than accidental. When migrating, these errors form a natural todo list: annotate each site with a real type instead of suppressing the flag.
Interviewers also ask
- Why is a silent any worse than an explicit one?
How do include, exclude, and lib work in tsconfig?basic
include and exclude choose which files the compiler checks — typically your src folder minus tests or build output. lib selects the built-in APIs available, like DOM for browser code or ES2022 for modern runtime helpers. Narrowing these keeps checking fast and prevents server code from accidentally using browser globals. Review them when adding a new folder or runtime target.
Interviewers also ask
- Why would server code exclude DOM lib?
What do target and module control?intermediate
target sets the JavaScript version emitted — how modern the syntax and helpers in your .js output may be. module sets the import format used between emitted files, like ESNext for bundlers or CommonJS for older Node setups. Match them to your runtime: modern browsers and Node accept higher targets, while libraries often compile lower for compatibility. Mismatched settings cause missing helpers or unloadable output.
Interviewers also ask
- What breaks if target is newer than your runtime?
How do noUnusedLocals and noUnusedParameters help?intermediate
They turn dead code into compile errors: unused local variables and unused function parameters are flagged instead of lingering. That keeps diffs clean and surfaces leftover scaffolding from refactors. Prefix a deliberately unused parameter with _, like _next, to signal intent and silence the check. Enable both once strict is green — they are the cheapest tidy-up automation a codebase can have.
Interviewers also ask
- How do you mark an intentionally unused parameter?
Key takeaways
strict: truearms the flag family — null checks and no silentanyfirst.targetis emit level,libis available built-ins; they are independent.- Legacy code: enable flags incrementally, never surrender globally.
Runner helpers
sleep(ms)waits;fakeFetch(url)returns deterministic sample data. TypeScript is stripped before running; imports and exports are not available in the sandbox.