Union & Intersection Types — The Logic of Data
Sometimes data is this or that. Sometimes it's this and that. Master Union (OR) and Intersection (AND) types to perfectly model complex real-world data.
📖 The Story: The Format Fiasco
A backend API was updated to support both string IDs ("user_123") and numeric IDs (123). The frontend developer wrote a function to display the ID: function printId(id: string) { console.log(id.toUpperCase()) }.
Immediately, the app crashed when a numeric ID was passed. The developer tried to fix it by changing the parameter to id: any, but then id.toUpperCase() crashed on numbers because numbers don't have that method.
The solution was a Union Type. By defining the parameter as id: string | number, TypeScript forced the developer to check if id was a string before calling .toUpperCase(). The crash was prevented before the code was even run.
Sometimes data can be multiple things. Union types make TypeScript aware of the possibilities.
🎯 Why Combine Types?
Union (OR)
Allow a variable to be one of several types (e.g., string or number).
Intersection (AND)
Combine multiple types into one super-type that has all properties.
Safe Narrowing
Force developers to check the type before using type-specific methods.
🧒 Explain Like I'm 10
Imagine you are ordering a combo meal. You can choose a burger OR a hot dog (Union). But no matter what you choose, it comes WITH fries AND a drink (Intersection).
A union type is the choice (Burger | HotDog). An intersection type is the full combo plate (Burger & Fries & Drink).
🎓 Professional Explanation
A Union Type uses the | operator to say a value can be one of several types. An Intersection Type uses the & operator to combine multiple types into one. When you use a union type, TypeScript requires you to narrow the type (using typeof, in, or instanceof) before you can access properties specific to one of the types.
🤝 Union Types (OR)
function printId(id: string | number) { console.log("Your ID is: " + id); } printId(101); // OK printId("202"); // OK printId(true); // Error: boolean not assignable
🔍 Type Narrowing
If you have a union type, you can't immediately access methods specific to a string or a number. You must narrow it down.
function formatId(id: string | number) { // Error: Property 'toUpperCase' does not exist on type 'string | number'. // console.log(id.toUpperCase()); // Narrowing using typeof if (typeof id === "string") { console.log(id.toUpperCase()); // TS knows it's a string here! } else { console.log(id.toFixed(2)); // TS knows it's a number here! } }
The in operator is used to narrow objects: if ("isAdmin" in user) { ... }.
🔗 Intersection Types (AND)
Intersections combine multiple types. The result must satisfy all combined types.
interface BusinessPartner { name: string; creditLimit: number; } interface Contact { email: string; phone: string; } // Must have name, creditLimit, email, AND phone type Vendor = BusinessPartner & Contact; const myVendor: Vendor = { name: "Acme Corp", creditLimit: 10000, email: "sales@acme.com", phone: "555-1234" };
📜 Literal Types
Unions are often used with literal types to define exact values a variable can hold.
type Direction = "North" | "South" | "East" | "West"; function move(direction: Direction) { // ... } move("North"); // OK move("Up"); // Error
⚠️ Common Mistakes
Mistake 1: Assuming Union Means "Any Property"
If you have TypeA | TypeB, you can only access properties that exist on both TypeA and TypeB. To access a property unique to TypeA, you must narrow it first.
✅ Best Practices
- Use Discriminated Unions: Give objects a common property (like
type: 'success') to make narrowing easier. - Avoid intersecting incompatible types: Intersecting
string&numberresults innever.
🏗️ Senior Deep Dive
Discriminated Unions (The Holy Grail)
This is the most powerful pattern in TypeScript for managing state (like Redux or API responses).
type NetworkState = | { state: "loading" } | { state: "failed", error: string } | { state: "success", data: string }; function logger(state: NetworkState) { switch (state.state) { case "loading": return "Loading..."; case "failed": return state.error; // TS knows 'error' exists here! case "success": return state.data; // TS knows 'data' exists here! } }
💼 Interview Questions
| and & in TypeScript?Show Answer
| creates a Union type (OR logic), allowing a value to be one of several types. & creates an Intersection type (AND logic), combining multiple types into one that must have all properties.Show Answer
typeof, in, or instanceof), allowing safe access to type-specific properties.🏋️ Practical Exercise
Write the Narrowing Logic
Given this function, write the if statement to safely call .toFixed(2) if the value is a number.
function processValue(value: string | number) { // Write your if statement here }
📝 Quiz
Test your understanding. Click an option to check your answer.
|&&&&) is used to combine types.📦 Summary
🎯 Key Takeaways
- Union Types (
|) allow a variable to be one of several types. - Intersection Types (
&) combine multiple types into one. - You must narrow union types using
typeoforinbefore accessing specific properties. - Discriminated Unions are the best pattern for managing complex state.
Comments
Comments
Post a Comment