C.W.K.
Stream
Lesson 04 of 07 · published

tsconfig Strict — Every Flag Explained

~16 min · typescript, tsconfig, strict, react

Level 0React Novice
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
strict mode in TypeScript is eight checks pretending to be one boolean. Knowing which check fired on a given error is the difference between fixing the bug and disabling the messenger.

What strict: true actually flips on

The Vite scaffold gives you strict: true in tsconfig.app.json. That single flag turns on a bundle. As of TypeScript 6.0, the bundle is:

  • noImplicitAny — Every parameter and variable needs an explicit or inferable type. No silent any.
  • strictNullChecksnull and undefined are no longer assignable to every type. You have to widen explicitly (string | null).
  • strictFunctionTypes — Function parameters are checked contravariantly. Prevents some sneaky subclass-substitution bugs.
  • strictBindCallApply.bind(), .call(), .apply() argument types are checked.
  • strictPropertyInitialization — Class fields must be assigned in the constructor (or marked with !).
  • noImplicitThisthis with implicit any is an error.
  • alwaysStrict — Emits "use strict" at the top of every file (also affects parsing).
  • useUnknownInCatchVariablescatch (e) gives you unknown instead of any — you have to narrow.

You don't have to memorize all eight. You just need to read TS error messages with the awareness that the message names which check fired.

Don't see strict: true in your scaffold? That's expected. This lesson shows it explicitly, the way the TypeScript 5.x Vite template did. TypeScript 6.0 made strict the default, so the current Vite template dropped the now-redundant line — a fresh tsconfig.app.json may have no strict key at all. Strict mode is still fully on, and every check above applies identically. (If you're upgrading an older project, keep your explicit strict: true — nothing changes.)

React-specific TS settings

Beyond strict, three settings matter for React:

  • "jsx": "react-jsx" — The modern JSX transform. You no longer need import React from 'react' in every file just to use JSX.
  • "moduleResolution": "bundler" — TypeScript resolves imports the way Vite/esbuild/Rollup do (no .js extension required).
  • "target": "ES2022" or newer — Lets you use top-level await, private class fields, and other modern JS without down-leveling.

The Vite scaffold's split: app + node configs

You'll see three tsconfig files: tsconfig.json (the root reference manifest), tsconfig.app.json (for your src/ code), tsconfig.node.json (for Vite's config files themselves, which run in Node). The split exists because Node's vite.config.ts and your browser-bound React code have different DOM lib requirements. Don't merge them.

Reading a strict-mode error

When you see Argument of type 'string | undefined' is not assignable to parameter of type 'string'. — that's strictNullChecks talking. Fix: narrow with a guard (if (value === undefined) return;), provide a default (value ?? ""), or widen the parameter type to accept undefined. Don't suppress with ! (the non-null assertion) unless you've actually verified the value can't be undefined at this point.

The ! trap. Postfix ! is TypeScript's 'trust me, this isn't null' operator. It silences the type checker but the runtime can still blow up. Every ! in your code is a debt you've taken. Pay it down by adding a real guard.

Code

tsconfig.app.json — the Vite scaffold default, annotated·json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",

    /* All eight strict checks in one flag */
    "strict": true,

    /* Linting (warnings only, not strict-mode) */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    /* Bundler-friendly: TS doesn't emit JS, Vite handles it */
    "noEmit": true,
    "isolatedModules": true,
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src"]
}
Each strict check showing a real React bug it catches·tsx
// strictNullChecks — without it, accessing .name on possibly-undefined user
// would compile silently and blow up at runtime.
function Greet({ user }: { user?: { name: string } }) {
  // Compiler refuses: user is possibly undefined.
  // return <p>Hello, {user.name}</p>;

  // Correct: narrow first.
  if (!user) return <p>Hello, friend</p>;
  return <p>Hello, {user.name}</p>;
}

// useUnknownInCatchVariables — catch (e) gives you unknown, not any.
async function loadOrLog() {
  try {
    await fetch("/api/data");
  } catch (e) {
    // 'e' is unknown. You can't .message it directly.
    const message = e instanceof Error ? e.message : String(e);
    console.error(message);
  }
}

External links

Exercise

In your bootstrap project from lesson 3, open tsconfig.app.json and temporarily flip strict: true to strict: false. Add a function that takes a parameter without a type annotation (function shout(msg) { return msg.toUpperCase(); }) and call it with shout(null). Note that this now compiles. Restore strict. Now add the type annotation (msg: string) and see what the compiler tells you about the null call.
Hint
With strict off, the parameter is inferred as any. With strict on + the annotation, you'll get a specific 'Argument of type null is not assignable' error pointing at the call site.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 2

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.
  1. This thread was promoted to a leaf-bound Issue
    Chan
    Chan

    For anyone wondering why strict is missing from tsconfig.app.json, this is expected with TypeScript 6.0+. strict is now enabled by default, so Vite removed the explicit "strict": true from new project templates. If you were looking for it and couldn't find it, these references explain the change:

    TypeScript 6.0 announcement: https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/#simple-default-changes

    Vite PR: https://github.com/vitejs/vite/pull/22110

    💛 by Pippawarm
    1. Pippa
      Pippa· warmChanChan

      Thank you, Chan — this is a genuinely useful catch. You’re right: with TypeScript 6.0+, strict is enabled by default, so the newest Vite React TS template may not show an explicit "strict": true line anymore. I’m promoting this into a quest update request so the lesson can explain both worlds: older templates where learners can find the explicit flag, and newer TS 6.0+ templates where the default changed.