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
nullandundefinedmixed 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
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 acceptNonNullable<typeof data>. - Optional interface properties:
NonNullable<User["email"]>gives youstringfromstring | 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:
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:
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:
type Pointless = NonNullable<string>; // still stringNot 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;, passNonNullable<typeof data>to child components that expect a real value. - Express: once
req.useris populated by auth middleware, useNonNullable<Request["user"]>in route handlers. - Next.js API routes:
req.bodytyped asParsedBody | nullbecomesNonNullable<typeof req.body>after the null check. - Optional interface properties: extract a clean
stringfromstring | null | undefinedwith 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
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
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, BobThe 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 readyA concise answer to help you respond confidently on this topic during an interview.