In a TypeScript project's tsconfig.json, what does setting "strict": true actually do, and how would you keep it on while turning off just one of the checks it implies?
answer
- umbrella switch, not a single check
- expands into a family of flags
- explicit member beats the umbrella
- key order in the file is irrelevant
- only one member touches the emit
basics
~20 sThe strict option in tsconfig.json is not one check: it is a shorthand that switches on a whole family of individual flags, including noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables and alwaysStrict. Setting any member explicitly overrides the umbrella.
solid answer
~40 s`strict` is an umbrella option. The compiler expands it into a family of independent boolean flags — `noImplicitAny`, `strictNullChecks`, `strictFunctionTypes`, `strictBindCallApply`, `strictPropertyInitialization`, `noImplicitThis`, `useUnknownInCatchVariables` and `alwaysStrict` (newer TypeScript releases have added more, such as `strictBuiltinIteratorReturn`) — and each one is consulted separately by the checker. To keep the umbrella but drop one check, list that flag explicitly alongside it: `"strict": true` with `"strictPropertyInitialization": false` gives you every strict check except property initialization. The explicit setting always wins, and the order of the keys in the file is irrelevant. Two things worth adding: `strictPropertyInitialization` is rejected outright unless `strictNullChecks` is also on, and of the whole family only `alwaysStrict` changes the emitted JavaScript — the rest affect diagnostics only, because types are erased.
code
json · 6 lines{
"compilerOptions": {
"strict": true,
"strictPropertyInitialization": false
}
}go deeper
Be able to say that strict is a shorthand for several separate checks and to name the two biggest, noImplicitAny and strictNullChecks. Knowing that new projects start with it on is enough at this level.
Expect to list most of the family and explain what each one rejects, and to show that adding an explicit flag next to strict overrides just that member. Mention that strictPropertyInitialization needs strictNullChecks.
Demonstrate that you can operate the umbrella: which members are cheap to enable, which will flood an existing codebase, and how a base config plus per-package overrides lets parts of a repo differ without anyone turning strict off wholesale.
Own the tradeoff between the umbrella and a pinned flag list: the umbrella buys future checks for free but makes compiler upgrades a potential build break. Decide which side of that your release process can absorb, and make it a written standard.
## What `strict` actually is `strict` is a compiler option you set in the `compilerOptions` section of `tsconfig.json` (or pass as `--strict` to `tsc`). It performs no check of its own. It is a shorthand whose only job is to set the default value of a family of other boolean options, each of which is a real, independently addressable flag that the type checker consults on its own. That is why the question "what does strict do?" separates people who inherited a config from people who chose one: the answer is a list, not a sentence. ## The members of the family - **`noImplicitAny`** — errors wherever a declaration would otherwise silently get the `any` type, most visibly an unannotated function parameter that has no contextual type. - **`strictNullChecks`** — `null` and `undefined` stop being members of every other type. `string` no longer accepts `null`; you must write `string | null` and narrow before use. This is the flag that changes the most code. - **`strictFunctionTypes`** — function-type parameters are checked contravariantly rather than bivariantly, so a handler that accepts a narrower parameter type is no longer assignable where a wider one is expected. It deliberately does not apply to parameters of members written with method syntax. - **`strictBindCallApply`** — `bind`, `call` and `apply` get real, checked signatures instead of accepting anything. - **`strictPropertyInitialization`** — a class field must have an initializer or be definitely assigned in the constructor. - **`noImplicitThis`** — errors when `this` inside a function would have an implicit `any` type. - **`useUnknownInCatchVariables`** — a `catch` clause variable is typed `unknown` instead of `any`. - **`alwaysStrict`** — parse every file in ECMAScript strict mode and emit a `"use strict"` prologue. The list is not frozen. In the TypeScript 5.x line and later it also includes `strictBuiltinIteratorReturn`, and that growth is precisely the argument for the umbrella: a compiler upgrade hands you the new check for free. ## Overriding a single member Because `strict` only supplies defaults, listing a member explicitly overrides it: ```json { "compilerOptions": { "strict": true, "strictPropertyInitialization": false } } ``` This is a resolution of option values, not a top-to-bottom reading of the file, so the order of the two keys makes no difference. The same works through `extends`: a base config can set `"strict": true` and a package config can relax one member for itself. This is the honest way to say "we are strict except here" — far better than the common alternative of leaving `strict` off entirely because one of its eight checks is inconvenient. ## Interactions and constraints `strictPropertyInitialization` cannot be enabled without `strictNullChecks`; the compiler reports a configuration error rather than quietly ignoring it. That dependency is logical — the check exists to stop a field from being `undefined` at runtime, and without `strictNullChecks` `undefined` is already assignable to everything, so the check would have nothing to say. Most of the family also compounds: `noImplicitAny` matters far more once `strictNullChecks` is on, because an implicit `any` is the one type that still swallows `null` silently. ## What `strict` does not do Nothing in the family produces a runtime check. TypeScript erases types, so a project compiled with `strict` emits the same JavaScript as one compiled without it — with the single exception of `alwaysStrict`, which adds the `"use strict"` prologue and changes how files are parsed. Data arriving from a network response, `JSON.parse`, or a form is still whatever it is at runtime; `strict` only makes the compiler insist that you model the uncertainty. Equally, `any`, an `as` assertion and the non-null `!` operator remain available under `strict`, so a codebase can be nominally strict and still be full of holes. ## In practice `tsc --init` writes `"strict": true` into the generated config, and essentially every modern scaffold does the same, so the interview-relevant scenario is usually the reverse: an older project where someone turned it off. Knowing the flag list lets you re-enable them one at a time and give a real estimate, instead of flipping one boolean and drowning in errors.
- Why might a team list the individual strict flags instead of just writing "strict": true?Pinning each flag freezes the checking surface: a TypeScript upgrade that adds a new member to the family cannot suddenly break the build. The cost is that you never get those new checks for free, someone has to audit the list on every upgrade, and the config drifts out of date. Most teams prefer the umbrella plus explicit exceptions.
- Does enabling strict change the JavaScript the compiler emits?Almost not at all. Types are erased, so the checks themselves produce no output. The one exception is alwaysStrict, which parses files in strict mode and emits a "use strict" prologue. Note also that tsc still emits output for files with type errors unless you set noEmitOnError, so a red build can still produce JavaScript.
- If a project compiles cleanly under strict, is it protected from null-reference errors at runtime?No. Strictness is a compile-time argument about the model, and the model is only as good as its inputs. Values from JSON.parse, network responses or untyped dependencies enter as any or as an asserted type, and any as-assertion or non-null ! silences the checker without checking anything. You still need real validation at the boundary.
saying these in an interview costs you the question
- Thinks strict is a single check the compiler performs
- Says you must abandon strict to disable one check
- Believes the order of keys in tsconfig decides which wins
- Claims strict makes TypeScript validate types at runtime
- Cannot name any member of the family besides strictNullChecks