Dealing with Reality: Optional Properties
Not all data is created equal
So far, we've defined object shapes where every property is required. But in the real world, data is often messy and incomplete. Some users have a middle name, others don't.
If we make every property mandatory, our types become inflexible. TypeScript gives us a clean way to handle this: the optional property modifier (?
).
The ?
Modifier for Properties
By adding a question mark ?
before the colon in a property definition, you tell TypeScript that the property is allowed to be missing.
middleName?: string;
This now correctly models our data: a User
must have a firstName
and lastName
, but middleName
is optional.
Exercise 1: Make it Optional
- The
Product
type below requires adiscountCode
. - The
tShirt
object doesn't have adiscountCode
, so TypeScript shows an error. - Fix the
Product
type by making thediscountCode
property optional.
The Catch: Safely Accessing Optional Properties
Making a property optional means its type is now a union with undefined
(e.g., string | undefined
). If you try to use it directly, you'll get an error because you might be calling a method on undefined
.
// ❌ Error: 'product.discountCode' is possibly 'undefined'.
console.log(product.discountCode.toUpperCase());
You asked a brilliant question: "Why can't I just put a ?
there too?"
You absolutely can. This is a modern JavaScript feature called Optional Chaining, and it's the perfect tool for this job.
Solution: The Optional Chaining Operator (?.
)
The optional chaining operator ?.
is a clean, short way to safely access properties of an object that might be null
or undefined
.
product.discountCode?.toUpperCase()
This single line means: "If product.discountCode
exists, call .toUpperCase()
on it. If it doesn't exist, just stop and evaluate the whole expression to undefined
." It's a built-in if
check.
Exercise 2: Use Optional Chaining
- The function
getDiscount
tries to access.toUpperCase()
onproduct.discountCode
, which causes an error. - Fix the error by using the optional chaining operator
?.
to safely access the property.
Click to see the solution
export {};
type Product = {
name: string;
price: number;
discountCode?: string;
};
function getDiscount(product: Product) {
// ✅ Correct!
return product.discountCode?.toUpperCase();
}
While optional chaining is perfect for one-liners, you can still use a traditional if
block if you need to handle the else
case or perform more complex logic. Both are valid, but optional chaining is often cleaner.
// The 'if' block version
if (product.discountCode) {
console.log(product.discountCode.toUpperCase());
} else {
console.log("No discount today!");
}