TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
23: Optional Properties
How do I tell TypeScript that a property isn't strictly required?
In the real world, your data is rarely perfect. If you're building a user profile system, every user has a username, but not every user is going to write a bio or link their Twitter account. If you define those as required strings, TypeScript will force you to provide them every single time you create a user object, which is a pain.
To fix this, we use the question mark ? right after the property name in the interface or type alias. This marks the property as "optional."
interface UserProfile {
username: string;
bio?: string; // This is optional
website?: string; // This is also optional
}
const userA: UserProfile = { username: "dev_jane", bio: "Code and Coffee" };
const userB: UserProfile = { username: "coding_bob" }; // No bio or website, and TS is happy!
I usually recommend making as few things optional as possible, though. If you make everything optional, you're basically just writing vanilla JavaScript again and losing the safety that makes TypeScript worth using.
Does an optional property just mean it's "undefined"?
Pretty much, yes. Under the hood, when you mark a property as bio?: string, TypeScript actually treats it as string | undefined.
The key difference is what happens during object creation. If you used a union type like bio: string | undefined, you would be forced to explicitly write bio: undefined when creating the object. With the optional ? syntax, you can just leave the key out entirely. It's a quality-of-life improvement that makes your object literals much cleaner.
How do I use these properties without the compiler complaining?
This is where most people hit a wall. Once you've marked a property as optional, TypeScript will stop you from using it directly because it knows the value might be undefined. If you try to call user.bio.toUpperCase(), the compiler will yell at you: "Object is possibly 'undefined'."
You have two main ways to handle this. The old way is a standard if check, but the modern, professional way is using optional chaining (the ?. operator). It tells TypeScript: "Try to access this property; if it's missing, just stop and return undefined instead of crashing the app."
function printBio(user: UserProfile) {
// This would throw an error if bio is missing:
// console.log(user.bio.toUpperCase());
// This is safe. If bio is undefined, the whole expression becomes undefined.
console.log(user.bio?.toUpperCase());
}
When should I use an optional property versus a union type with null?
This is a common architectural debate. In my experience, use ? when the property is truly optional to provide (like an optional setting in a config file). Use string | null when the property must exist, but its value might be intentionally empty or "cleared."
For example, in a database record, a deletedAt timestamp might be null if the record is active. You wouldn't make that optional, because the field exists in the database schema—it just happens to be empty. If you omit the field entirely via ?, you're saying the property might not even exist on the object, which is a different semantic meaning.
📋 Practical Task
Refactoring the Product Catalog Interface
You are working on an e-commerce site. Currently, the Product interface requires every single field, which is causing crashes when adding simple products that don't have a discount or a technical specification list.
Your Task:
- Modify the
Productinterface so thatdiscountPriceandspecsare optional. - Fix the
displayProductfunction so it doesn't throw a TypeScript error when trying to access the length of thespecsarray.
interface Product {
name: string;
price: number;
discountPrice: number; // Make this optional
specs: string[]; // Make this optional
}
function displayProduct(product: Product) {
console.log(`Product: ${product.name}`);
// FIX THIS LINE: It currently errors because specs might be undefined
console.log(`Specifications count: ${product.specs.length}`);
}
const basicLamp: Product = {
name: "Desk Lamp",
price: 25,
// discountPrice and specs are missing here, which should now be allowed
};
displayProduct(basicLamp);There are no comments for now.