Skip to main content

Utility type nonnullable in TypeScript

NonNullable<T> removes null and undefined from type T, leaving only the values that can actually be there.

Theory

TL;DR

  • Think coffee filter: you pour in a type with null and undefined mixed in, and get back only the real type.
  • Implementation: T extends null | undefined ? never : T - TypeScript distributes this over each union member.
  • Use after a null guard (if (!user) return), not instead of one.
  • It only strips top-level null/undefined. Nested nulls inside objects survive.
  • Semantically identical to Exclude<T, null | undefined>, but the name signals intent better.

Quick example

typescript
type MaybeUser = { name: string } | null | undefined; type User = NonNullable<MaybeUser>; // { name: string } // Practical use: after a null check function greet(user: User) { console.log(user.name); // no optional chaining needed }

The type tells TypeScript: "by the time this value reaches this function, null is already handled."

Key difference from Exclude

NonNullable<T> and Exclude<T, null | undefined> produce the same result for standard unions. The difference is intent. NonNullable signals "I guarantee this is non-null" in a code review, while Exclude reads as a generic subtraction. Pick NonNullable when your goal is specifically removing nullability.

When to use

  • After a runtime null guard: if you do if (!data) return;, the next function can accept NonNullable<typeof data>.
  • Optional interface properties: NonNullable<User["email"]> gives you string from string | undefined.
  • Array filtering with proper type narrowing (the Examples section shows this).
  • Post-validation in API handlers, after confirming a response exists.

Avoid wrapping raw type definitions in NonNullable. If a prop can be null, say so with | null. Reserve NonNullable for the narrowed, post-check side.

How the compiler handles this

TypeScript evaluates NonNullable<string | null | undefined> by distributing the conditional type over each union member. string extends null | undefined is false, so it stays. null extends null | undefined is true, so it becomes never. And never drops out of unions automatically. The whole thing is erased before TypeScript emits any JavaScript.

Common mistakes

Expecting deep null removal:

typescript
type HasInnerNull = { prop: string | null }; type Result = NonNullable<HasInnerNull | null>; // Result: { prop: string | null } <- inner null survives!

Only the top-level null is stripped. For deep removal, write a recursive DeepNonNullable mapped type or use Zod for runtime validation.

Treating it as a runtime check:

typescript
function bad(user: User | null) { const safe: NonNullable<typeof user> = user!; return safe.name; // compiles fine, crashes at runtime if user is null }

NonNullable is type-only. Always pair it with an actual guard before using the narrowed type.

Applying it when there is nothing to strip:

typescript
type Pointless = NonNullable<string>; // still string

Not an error, but it adds noise. Only use it when the type actually includes null or undefined.

Real-world usage

  • React Query: after if (!data) return null;, pass NonNullable<typeof data> to child components that expect a real value.
  • Express: once req.user is populated by auth middleware, use NonNullable<Request["user"]> in route handlers.
  • Next.js API routes: req.body typed as ParsedBody | null becomes NonNullable<typeof req.body> after the null check.
  • Optional interface properties: extract a clean string from string | null | undefined with a single type utility.

Follow-up questions

Q: Write the implementation of NonNullable<T>.
A: type NonNullable<T> = T extends null | undefined ? never : T;. TypeScript's standard library also has a version using T & {}, which produces the same result in practice.

Q: What is the difference between NonNullable<T> and Exclude<T, null | undefined>?
A: For union types, they produce identical results. NonNullable is the better choice when the intent is specifically removing nullability, because the name makes that obvious.

Q: Does NonNullable remove nulls from nested object properties?
A: No. It only strips top-level null and undefined from the union. NonNullable<{ val: string | null } | null> gives { val: string | null }. For deep cleaning, you need a recursive mapped type.

Q: How does it work with branded types?
A: Brands are preserved. NonNullable<(string & { readonly _brand: "UserId" }) | null> returns the branded type intact, because the brand does not extend null | undefined.

Examples

Extracting non-null from optional properties

typescript
interface User { id: string; name: string; email?: string; // string | undefined phone?: string | null; // string | null | undefined } type RequiredEmail = NonNullable<User["email"]>; // string type RequiredPhone = NonNullable<User["phone"]>; // string function sendConfirmation(email: RequiredEmail) { console.log(`Sending to ${email}`); // no null check needed here }

User["email"] resolves to string | undefined. After NonNullable, you get plain string. This is a common pattern when you need to pass an optional property to a function that expects a definite value.

Array filtering with type narrowing

typescript
const users: (User | null | undefined)[] = [ { id: "1", name: "Alice", email: "alice@example.com" }, null, { id: "2", name: "Bob", email: "bob@example.com" }, undefined, ]; // filter(Boolean) removes null/undefined at runtime, // but TypeScript still infers (User | null | undefined)[] // A type predicate fixes that: const validUsers = users.filter( (u): u is NonNullable<typeof u> => u != null ); // validUsers: User[] validUsers.forEach(u => console.log(u.name)); // Alice, Bob

The predicate u is NonNullable<typeof u> is what narrows the type. Without it, TypeScript keeps the nullable union even after filtering. In real code, I reach for this pattern any time I need to clean an array from an API response that might contain sparse null entries.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?