TypeScript Nullable Types
In TypeScript, null and undefined are distinct values with specific meanings. null means a value was intentionally set to empty. undefined means a variable was declared but never given a value. TypeScript treats both as types and lets developers control when a variable can or cannot hold these values.
null vs undefined — The Difference
REAL-WORLD ANALOGY
null → An empty plate placed at the table on purpose
(the chef decided to serve nothing)
undefined → No plate at the table at all
(no one set the table for this seat)
let orderStatus: string | null = null; // intentionally no status yet let deliveryDate: string | undefined; // never set — undefined by default console.log(orderStatus); // null console.log(deliveryDate); // undefined
strictNullChecks
The strictNullChecks option in tsconfig.json is the switch that makes TypeScript enforce null safety. With it off, any variable can accidentally hold null or undefined. With it on, the compiler catches these mistakes before the code runs.
// tsconfig.json
{
"compilerOptions": {
"strictNullChecks": true // recommended: always true
}
}
// With strictNullChecks ON:
let name: string = null; // Error: null is not assignable to type 'string'
// To allow null, declare it explicitly:
let name: string | null = null; // OK
Combining null and undefined with Types
// Three ways to allow nullable values
let a: string | null = null; // only null allowed
let b: string | undefined = undefined; // only undefined allowed
let c: string | null | undefined = null; // both allowed
function findUser(id: number): { name: string } | null {
if (id === 1) {
return { name: "Kavitha" };
}
return null; // user not found
}
let user = findUser(5);
// user could be { name: string } or null
// TypeScript will not let us use user.name without checking first
Checking Before Using Nullable Values
FLOW DIAGRAM
─────────────────────────────────────────────
let user = findUser(id);
│
├── null? → "User not found" (handle gracefully)
│
└── object? → use user.name (safe to access)
─────────────────────────────────────────────
// Code version
let user = findUser(5);
if (user !== null) {
console.log(user.name); // TypeScript: safe here
} else {
console.log("User not found");
}
Optional Function Parameters
A question mark after a parameter name makes it optional. Optional parameters are automatically type | undefined.
function greet(name: string, title?: string): string {
if (title !== undefined) {
return `Hello, ${title} ${name}!`;
}
return `Hello, ${name}!`;
}
console.log(greet("Rohan")); // Hello, Rohan!
console.log(greet("Sharma", "Dr.")); // Hello, Dr. Sharma!
Optional Object Properties
interface BookingForm {
passengerName: string;
flightNumber: string;
mealPreference?: string; // optional — may not be provided
specialAssistance?: string; // optional
}
let booking: BookingForm = {
passengerName: "Meena",
flightNumber: "AI-202"
// mealPreference and specialAssistance are fine to omit
};
// Check before using optional property
if (booking.mealPreference !== undefined) {
console.log(`Meal: ${booking.mealPreference}`);
}
Default Values for undefined
function createLabel(text: string, fontSize?: number): string {
// If fontSize was not provided, default to 14
let size = fontSize ?? 14;
return `<span style="font-size:${size}px">${text}</span>`;
}
console.log(createLabel("Hello")); // font-size: 14px
console.log(createLabel("Title", 24)); // font-size: 24px
The ?? (Nullish Coalescing) Operator
The ?? operator returns the right side only when the left side is null or undefined. Unlike ||, it does not treat 0 or empty string as falsy.
COMPARISON: || vs ?? ───────────────────────────────────────────────────── let score: number | null = 0; console.log(score || 10); // 10 — wrong! 0 is falsy, so || replaces it console.log(score ?? 10); // 0 — correct! 0 is not null/undefined let username: string | null = null; console.log(username ?? "Guest"); // Guest — null triggers ?? ─────────────────────────────────────────────────────
Practical Example: Student Record System
interface Student {
id: number;
name: string;
email: string;
phone?: string; // optional
parentContact: string | null; // may not have parent contact
}
function displayContact(student: Student): void {
console.log(`Name: ${student.name}`);
console.log(`Email: ${student.email}`);
console.log(`Phone: ${student.phone ?? "Not provided"}`);
if (student.parentContact !== null) {
console.log(`Parent: ${student.parentContact}`);
} else {
console.log("Parent contact: Not available");
}
}
let s1: Student = {
id: 1,
name: "Deepak",
email: "deepak@school.com",
phone: "9876543210",
parentContact: "9123456789"
};
let s2: Student = {
id: 2,
name: "Fatima",
email: "fatima@school.com",
parentContact: null
};
displayContact(s1);
// Name: Deepak
// Email: deepak@school.com
// Phone: 9876543210
// Parent: 9123456789
displayContact(s2);
// Name: Fatima
// Email: fatima@school.com
// Phone: Not provided
// Parent contact: Not available
Common Nullable Type Patterns
| Pattern | Code | Meaning |
|---|---|---|
| Nullable variable | let x: string | null | Can be a string or null |
| Optional property | age?: number | Can be number or undefined |
| Optional parameter | fn(x?: string) | Caller can skip this argument |
| Nullish coalescing | val ?? "default" | Use val unless it is null/undefined |
| Null check | if (x !== null) | Narrows type to remove null |
