//
Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Write React code that thrives under TypeScript's strictest settings. Master patterns for strictNullChecks, noUncheckedIndexedAccess, and exactOptionalPropertyTypes to catch bugs that looser settings miss.
// strictNullChecks: handling nullable values safely
type User = {
id: string;
name: string;
address?: {
street: string;
city: string;
};
};
function UserAddress({ user }: { user: User }) {
// Without strictNullChecks, this silently returns undefined
// With strictNullChecks, TypeScript forces you to handle the case
if (!user.address) {
return <p>No address on file</p>;
}
return (
<p>
{user.address.street}, {user.address.city}
</p>
);
}// noUncheckedIndexedAccess: safe array and object access
const colors = ["red", "green", "blue"];
// Without noUncheckedIndexedAccess: colors[5] is string
// With noUncheckedIndexedAccess: colors[5] is string | undefined
function getColor(index: number): string {
const color = colors[index];
if (!color) {
return "default";
}
return color; // TypeScript now knows this is string, not string | undefined
}// Safe record access
type FeatureFlags = Record<string, boolean>;
function isFeatureEnabled(flags: FeatureFlags, key: string): boolean {
const value = flags[key];
// With noUncheckedIndexedAccess, value is boolean | undefined
return value ?? false;
}strict: true enables a family of strict checks. The most impactful ones for React are strictNullChecks, strictFunctionTypes, and noImplicitAny.strictNullChecks makes null and undefined distinct types that must be handled explicitly. A variable typed as string cannot be null unless you type it as string | null.noUncheckedIndexedAccess adds | undefined to any index access on arrays and records. This catches the common bug of accessing an array index that might be out of bounds.exactOptionalPropertyTypes distinguishes between a property that is missing and one that is explicitly undefined. { name?: string } means "name can be absent" but not { name: undefined }.strictFunctionTypes enforces contravariant parameter checking on function types. This catches subtle bugs with event handler types and callback props.Safe map/object lookups:
const userMap = new Map<string, User>();
function getUser(id: string): User {
const user = userMap.get(id); // User | undefined
if (!user) {
throw new Error(`User ${id} not found`);
}
return user; // Narrowed to User
}Non-null assertion as escape hatch:
// Use sparingly and only when you are certain the value exists
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!; // Non-null assertion
// Better: guard and throw
const ctx2 = canvas.getContext("2d");
if (!ctx2) throw new Error("Canvas 2D context not available");Handling optional chaining with strict nulls:
type Config = {
api?: {
baseUrl?: string;
timeout?: number;
};
};
function getApiUrl(config: Config): string {
return config.api?.baseUrl ?? "https://api.example.com";
}exactOptionalPropertyTypes in action:
// With exactOptionalPropertyTypes enabled:
type Settings = {
theme?: "light" | "dark";
};
// Allowed
const a: Settings = {};
const b: Settings = { theme: "light" };
// Error: undefined is not assignable to "light" | "dark"
// const c: Settings = { theme: undefined };strict: true, then noUncheckedIndexedAccess, then exactOptionalPropertyTypes.?? (nullish coalescing) and ?. (optional chaining) operators are your best tools under strict settings. They handle null | undefined cleanly.Array.prototype.find() returns T | undefined even without noUncheckedIndexedAccess. The setting mainly affects bracket access arr[i].noUncheckedIndexedAccess adds | undefined to tuple access too. For tuples with known length, this can feel overly cautious. Use destructuring instead: const [first, second] = tuple.exactOptionalPropertyTypes breaks code that explicitly passes undefined to clear optional fields. You may need to use delete obj.field instead.!) bypass strict checks entirely. Every ! is a potential runtime crash. Prefer guards and narrowing.skipLibCheck: true helps, but it hides issues in your own .d.ts files.| Approach | Pros | Cons |
|---|---|---|
strict: true + noUncheckedIndexedAccess | Maximum safety, catches most bugs | More verbose null handling |
strict: true only | Good balance of safety and convenience | Misses array out-of-bounds bugs |
strict: false with incremental flags | Gradual migration path | Easy to miss important checks |
| Runtime validation (Zod) | Catches issues strict mode cannot | Added dependency, runtime overhead |
as assertions to bypass strictness | Quick fix for type errors | Defeats the purpose of strict mode |
strictNullChecks, strictFunctionTypes, noImplicitAny, strictBindCallApply, strictPropertyInitialization, and others.strictNullChecks, strictFunctionTypes, and noImplicitAny.string can no longer be null or undefined unless explicitly typed as string | null.| undefined to any index access on arrays and records.colors[5] becomes string | undefined instead of string.type Settings = { theme?: "light" | "dark" };
const a: Settings = {}; // OK: property missing
const b: Settings = { theme: "light" }; // OK
// const c: Settings = { theme: undefined }; // ERRORundefined are treated as distinct.undefined to an optional field.! is a potential runtime crash if the value actually is null or undefined.?.), or nullish coalescing (??) instead.| undefined on bracket access.const [first, second] = tuple avoids the | undefined.const colors = ["red", "green", "blue"];
function getColor(index: number): string {
const color = colors[index]; // string | undefined
if (!color) return "default";
return color; // narrowed to string
}undefined after index access.?? for providing fallback values.strict: true (enables the core family of checks).noUncheckedIndexedAccess (adds index safety).exactOptionalPropertyTypes (strictest optional property handling).strictNullChecks or exactOptionalPropertyTypes in mind.skipLibCheck: true helps by skipping type-checking of .d.ts files..d.ts files.?. short-circuits to undefined if the left side is null or undefined: config.api?.baseUrl.?? provides a fallback only for null or undefined: value ?? "default".||, ?? does not trigger on falsy values like 0 or "".Reviewed by Chris St. John·Last updated Jul 7, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥