Lesson 51 of 80
TypeScript's default behavior quietly treats 'this property is missing' and 'this property is present and set to undefined' as the same thing — a flag exists to tell them apart, and it changes real code.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
By default, age?: number on an interface means "the age property is either a number, or it's absent, or it's present with the value undefined — all three are fine." That third case is easy to miss: an optional property's type is secretly widened to include undefined as an assignable value, not just as "missing."
interface User {
name: string;
age?: number;
}
const a: User = { name: "Ada" }; // ✅ age absent
const b: User = { name: "Ada", age: undefined }; // ✅ age explicitly set to undefined
Both compile identically by default. exactOptionalPropertyTypes: true (a tsconfig.json flag) turns off that equivalence: age?: number now means either the key is completely absent, or it's present with an actual number — explicitly assigning undefined to it becomes a type error.
interface Settings {
theme?: "light" | "dark";
}
function applySettings(overrides: Settings, defaults: Settings) {
return { ...defaults, ...overrides };
}
const custom: Settings = { theme: undefined };
applySettings(custom, { theme: "dark" });
// with exactOptionalPropertyTypes off: { theme: undefined } — the spread silently
// overwrites "dark" with undefined, because `theme` really is a key on the object
// with exactOptionalPropertyTypes on: `custom` itself fails to compile —
// TypeScript refuses to let you construct that ambiguous object in the first place
Without the flag, { theme: undefined } is a perfectly valid Settings value — and then { ...defaults, ...overrides } does exactly what object spread always does: overwrite theme with undefined, silently discarding "dark". The bug isn't in the spread; it's that TypeScript let you construct an object that looks like "no opinion on theme" but actually carries a real, overwriting undefined value. With the flag on, that object is rejected at the point it's created — the error moves to where the ambiguity was actually introduced.
age?: number and age: number | undefined look similar but mean different things even with the flag on. The first still permits the key to be fully absent; the second requires the key to be present, just possibly holding undefined as its value. The flag makes this distinction actually enforced instead of cosmetic.{ ...obj, prop: cond ? value : undefined } to "conditionally" set a property — that pattern always sets the key, it just sometimes sets it to undefined, which is not the same as omitting it."What's the actual difference between an optional property and a property typed T | undefined?" is a question that separates people who've only used ? from people who've hit a real bug from the ambiguity — being able to explain the settings-merging failure mode above, and name the flag that catches it, is a strong, concrete answer.