---
title: "How to configure TypeScript strict mode"
slug: "configure-typescript-strict"
category: "howto"
tags: ["howto", "typescript", "strict-mode", "tsconfig", "tutorial", "coding"]
status: "stable"
last_updated: 2026-08-14
summary: "Enable strict in tsconfig.json, fix existing errors in priority order starting with noImplicitAny, add noUncheckedIndexedAccess, and verify the build is clean."
related: ["[[coding/typescript-strict-mode]]", "[[coding/typescript-tsconfig]]", "[[coding/typescript]]", "[[coding/typescript-narrowing]]", "[[coding/typescript-types-vs-interfaces]]", "[[coding/general-principles]]", "[[comparisons/typescript-vs-javascript]]"]
---

## Overview

Enabling `strict` in an existing TypeScript project surfaces errors that have been silently failing. Fixing them in the right order prevents a flood of 200 errors that paralyzes a team. Enable the flag, triage by error code, and fix the high-payoff errors first. The reference for what each strict flag does lives in [[coding/typescript-strict-mode]]; the full tsconfig options reference lives in [[coding/typescript-tsconfig]].

## Prerequisites

- TypeScript 4.9 or newer. Run `npx tsc --version` to check.
- A `tsconfig.json` at the project root. If you only have a `jsconfig.json`, rename it.
- The project builds and passes tests before you start. Do not add strict on top of pre-existing failures.

## Steps

### 1. Enable `strict` in `tsconfig.json`

`strict` is a shorthand that enables the full set of strict-family flags at once: `noImplicitAny`, `strictNullChecks`, `strictFunctionTypes`, `strictBindCallApply`, `strictPropertyInitialization`, `noImplicitThis`, `alwaysStrict`, `useUnknownInCatchVariables`, and (TypeScript 5.6+) `strictBuiltinIteratorReturn`. Run `npx tsc --version` first; on an older compiler the last flag is a no-op.

```json
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}
```

Run the type-checker immediately:

```bash
npx tsc --noEmit 2>&1 | tee tsc-errors.txt
wc -l tsc-errors.txt
```

### 2. Triage by error code

Sort errors by code to understand the scope before touching a single file:

```bash
grep "error TS" tsc-errors.txt | grep -oP 'TS\d+' | sort | uniq -c | sort -rn | head -20
```

Fix in this order:

1. **TS2322, TS2345** (type mismatch): usually wrong `any` types that need replacing.
2. **TS7006, TS7031** (`noImplicitAny` on parameters): add explicit types.
3. **TS2531, TS2532** (possibly null/undefined): add null checks or non-null assertions where safe.
4. **TS2564** (property not assigned in constructor): use definite assignment (`!`) only when initialization is guaranteed, or initialize in the constructor.
5. **TS18046** (`unknown` in catch blocks): replace `catch (e: any)` with `catch (e: unknown)` and narrow the type.

### 3. Fix `noImplicitAny` first

Implicit `any` is the root cause of most downstream errors. Replace `any` with real types wherever possible.

```ts
// Before: implicit any on parameter
function process(items) {
  return items.map(i => i.name);
}

// After: explicit type
function process(items: Array<{ name: string }>) {
  return items.map(i => i.name);
}
```

When the type is genuinely unknown at the call site, use `unknown` and narrow it; avoid `any`. See [[coding/typescript-narrowing]] for narrowing patterns.

### 4. Fix `strictNullChecks` errors

`strictNullChecks` rejects assignments of `null | undefined` to non-optional types. The fix is almost always to guard before use, not to add `!`.

```ts
// Before: possible null ignored
const user = getUser();
console.log(user.name); // TS2532: Object is possibly undefined

// After: explicit guard
const user = getUser();
if (!user) throw new Error("User not found");
console.log(user.name); // safe
```

Use `!` (non-null assertion) only when you have context the compiler does not, and add a comment explaining why.

### 5. Add `noUncheckedIndexedAccess`

This flag is not included in `strict` but closes the most common real-world runtime crash: reading an array index that may be `undefined`.

```json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true
  }
}
```

After adding it, array accesses return `T | undefined`. Update loops to guard the index:

```ts
const first = items[0]; // now typed as Item | undefined
if (first === undefined) return;
console.log(first.name);
```

### 6. Verify the build is clean

```bash
npx tsc --noEmit
# No output means zero type errors.

npm test
# All tests pass with stricter types.
```

## Verify it worked

```bash
# 1. tsc reports zero errors.
npx tsc --noEmit && echo "Clean"

# 2. The error count from step 2 is now zero.
npx tsc --noEmit 2>&1 | grep -c "error TS"
# 0

# 3. CI passes.
# Push to a branch and confirm the type-check step is green.
```

## Common errors

- **Hundreds of errors after enabling `strict`**. Enable flags one at a time: set `strict: false` and add `"noImplicitAny": true` first. Fix that batch, then add `"strictNullChecks": true`, and so on.
- **Third-party library causes errors**. The library lacks type definitions. Install `@types/<package>` or add `"skipLibCheck": true` temporarily while you file an upstream issue.
- **Non-null assertion (`!`) spreads through the codebase**. This defeats the purpose of `strictNullChecks`. Replace each `!` with a proper guard or restructure the code so null is not possible.
- **`strictPropertyInitialization` breaks classes with DI frameworks**. Decorators initialize properties outside the constructor. Use the `useDefineForClassFields: false` option with older decorators, or declare properties as `declare` to suppress the error.
- **Build passes but tests fail after strict changes**. The type guards changed runtime behavior. Review any code that narrows via `typeof`, `instanceof`, or explicit checks.

## Related

- [[coding/typescript-strict-mode]]
- [[coding/typescript-tsconfig]]
- [[coding/typescript]]
- [[coding/typescript-narrowing]]
- [[coding/typescript-types-vs-interfaces]]
- [[coding/general-principles]]
- [[comparisons/typescript-vs-javascript]]
