Advanced Types
🔷 Advanced Types
Union Types: Multiple Possibilities
A union type represents a value that could be one of several types. Use the | (pipe) operator:
String Literals
Restrict to specific string values. Great for enums-like behavior without the syntax overhead.
Union of Types
Mix primitives: string | number, or objects with different shapes.
Intersection Types: Combine Constraints
An intersection type combines multiple types into one. Use the & operator:
Union vs Intersection
Union (|)
Or: type can be one thing or another
Intersection (&)
And: type must have properties from both
Type Guards: Narrowing Union Types
When working with unions, TypeScript doesn't know which type you have. Type guards narrow the type so you can safely use type-specific operations:
typeof Guard
Check primitive types: string, number, boolean, undefined.
instanceof Guard
Check if a value is an instance of a class: obj instanceof Error.
Custom Guard
Write a function with return type value is Type for complex checks.
in Operator
Check if an object has a property: "email" in user.
Custom Type Guard Example
Conditional Types: Types That Decide
Conditional types use the ternary operator to assign a type based on a condition:
Key keyword: infer captures a type that will be inferred during type checking.
Mapped Types: Transform Types Systematically
Mapped types let you create new types by transforming properties of existing types:
keyof
Gets all property names of a type as a union. keyof User → "id" | "name" | "email"
in
Iterates over the union. Creates a new property for each.
[K]
Accesses the type of a property. User[K] gets the type of property K.
Utility Types
TypeScript provides built-in mapped types: Partial<T>, Readonly<T>, Record<K, V>
Practical Mapped Type: Read-Only Config
🏗️ Real-World Patterns
Pattern 1: Discriminated Unions (Tagged Unions)
Use a common property to distinguish between union members:
Pattern 2: Extract Properties with Conditional Types
💻 Coding Challenges
Challenge 1: Union & Type Guard
Write a function that accepts either a User object or a user id (number). Inside, use a type guard to determine which was passed and handle each case differently.
Goal: Practice unions and type narrowing with typeof.
Challenge 2: Intersection & Custom Guard
Create two interfaces (e.g., Admin and Employee), then a type for a value that is both. Write a custom type guard function to check if something is an Admin.
Goal: Practice intersections and custom type predicates.
Challenge 3: Mapped Type
Take any interface and create a mapped type that makes all properties optional and readonly. Test it with a sample object.
Goal: Build comfort with keyof, in, and property transformations.
Avoid unions that are too broad — they defeat the purpose of type safety. For example, string | number | boolean | object | any basically gives up on type checking. Use discriminated unions and type guards to narrow aggressively. The more specific your types, the more the compiler can help you.
🎯 What's Next
Now that you've mastered advanced types, we'll explore Generics Deep Dive — how to write reusable, type-safe code that works across different types. This is where TypeScript truly shines.