๐งฉ TypeScript Type Composition: Building with LEGO Blocks
Imagine you have a box of LEGO blocks. Each block has a shape and color. Now, what if you could combine blocks in special ways to build amazing things? Thatโs exactly what Type Composition is in TypeScript!
๐ฏ The Big Idea
Think of types like ingredients in a kitchen. Sometimes you need just one ingredient (a simple type). But often, you want to mix ingredients together to make something new and delicious!
TypeScript gives you special tools to combine types:
- Mix them together (Union)
- Stack them on top of each other (Intersection)
- Lock them so nobody changes them (readonly/const)
Letโs explore each one!
๐ Union Types: โThis OR Thatโ
What is it?
A union type says: โThis can be one thing OR another thing.โ
Think of a traffic light. It can be:
- ๐ด Red
- ๐ก Yellow
- ๐ข Green
But it can ONLY be ONE color at a time!
Example
type TrafficLight = "red" | "yellow" | "green";
let light: TrafficLight = "red"; // โ
OK
light = "green"; // โ
OK
light = "purple"; // โ Error!
Real Life Use
type Pet = "cat" | "dog" | "fish";
type Age = number | string;
let myPet: Pet = "cat"; // โ
let myAge: Age = 10; // โ
number works
myAge = "ten years old"; // โ
string works too!
๐จ Visual Flow
graph TD A[Union Type] --> B[Option 1] A --> C[Option 2] A --> D[Option 3] B --> E[Pick ONE!] C --> E D --> E
Remember: Union = Choose ONE from many options!
โ Intersection Types: โThis AND Thatโ
What is it?
An intersection type says: โThis must be everything combined!โ
Imagine a superhero who has:
- Flying powers (like Superman)
- Spider powers (like Spider-Man)
With intersection, your hero gets BOTH powers at once!
Example
type CanFly = {
fly: () => void;
};
type CanSwim = {
swim: () => void;
};
type SuperHero = CanFly & CanSwim;
const aquaFlyer: SuperHero = {
fly: () => console.log("Whoosh!"),
swim: () => console.log("Splash!")
};
๐จ Visual Flow
graph TD A[Type A] --> C[Intersection A & B] B[Type B] --> C C --> D[Has ALL properties!]
Remember: Intersection = Get EVERYTHING from all types!
๐ Literal Types: Exact Values Only
What is it?
A literal type is super strict. It says: โOnly THIS exact value is allowed!โ
Itโs like a password. Not just any wordโonly the EXACT word works.
Example
type Yes = "yes";
type Five = 5;
type IsTrue = true;
let answer: Yes = "yes"; // โ
Only "yes" works
answer = "Yes"; // โ Error! Capital Y!
answer = "yeah"; // โ Error! Not exact!
let count: Five = 5; // โ
Only number 5
count = 6; // โ Error!
Combining Literals
type Direction = "up" | "down" | "left" | "right";
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
let move: Direction = "up"; // โ
let roll: DiceRoll = 4; // โ
roll = 7; // โ Error!
๐ค Template Literal Types: Smart Text Building
What is it?
Template literals let you build new text types from pieces!
Itโs like Mad Libsโyou fill in the blanks to create new sentences.
Example
type Color = "red" | "blue" | "green";
type Size = "small" | "large";
type TShirt = `${Size}-${Color}`;
// Creates: "small-red" | "small-blue" |
// "small-green" | "large-red" |
// "large-blue" | "large-green"
let myShirt: TShirt = "small-red"; // โ
myShirt = "medium-red"; // โ Error!
๐จ Visual Flow
graph TD A[Size: small, large] --> C[Template] B[Color: red, blue, green] --> C C --> D["small-red"] C --> E["small-blue"] C --> F["large-red"] C --> G["...and more!"]
๐ readonly vs const: Locking Things Down
Whatโs the difference?
Think of a museum:
- const = The display case is bolted to the floor (canโt move it)
- readonly = The items INSIDE the case canโt be touched
const Example
const myName = "Alex";
myName = "Bob"; // โ Error! Can't change!
const myPets = ["cat", "dog"];
myPets.push("fish"); // โ
Array content CAN change!
myPets = []; // โ Can't reassign!
readonly Example
type Person = {
readonly name: string;
age: number;
};
const me: Person = { name: "Alex", age: 10 };
me.age = 11; // โ
age can change
me.name = "Bob"; // โ Error! name is readonly!
Quick Comparison
| Feature | const | readonly |
|---|---|---|
| What it locks | The variable itself | Property inside object |
| Can reassign? | โ No | โ No (that property) |
| Array push? | โ Yes | โ No (if readonly array) |
๐ฆ Destructuring with Types
What is it?
Destructuring is like unpacking a gift box. You take things OUT and give them names!
Object Destructuring
type Gift = {
toy: string;
color: string;
price: number;
};
const present: Gift = {
toy: "robot",
color: "blue",
price: 20
};
// Unpack the gift!
const { toy, color }: Gift = present;
console.log(toy); // "robot"
console.log(color); // "blue"
Array Destructuring
const scores: [number, number, number] = [95, 87, 92];
const [first, second, third] = scores;
console.log(first); // 95
With Default Values
type Settings = {
theme?: string;
volume?: number;
};
const config: Settings = { theme: "dark" };
const {
theme = "light",
volume = 50
}: Settings = config;
console.log(theme); // "dark" (from config)
console.log(volume); // 50 (default used!)
๐ค Object vs object vs {}
This is tricky! Letโs make it simple:
The Three Types
| Type | What it means | Example |
|---|---|---|
Object |
Almost anything (avoid!) | String, Number, Array, {} |
object |
Only non-primitives | {}, [], function |
{} |
Any non-null/undefined | Almost everything! |
Visual Guide
graph TD A["{} - Widest"] --> B["object - Middle"] B --> C["Specific type - Best!"] D["Object - Avoid!"] -.-> A
Examples
// Object (capital O) - TOO broad, avoid!
let a: Object = "hello"; // โ
string works
let b: Object = 42; // โ
number works
let c: Object = {}; // โ
object works
// object (lowercase) - non-primitives only
let d: object = {}; // โ
object literal
let e: object = []; // โ
array is object
let f: object = "hello"; // โ Error! string!
// {} - almost anything except null/undefined
let g: {} = "hello"; // โ
works
let h: {} = 42; // โ
works
let i: {} = null; // โ Error!
๐ Best Practice
// โ Avoid these - too vague
let bad1: Object = {};
let bad2: object = {};
let bad3: {} = {};
// โ
Use specific types!
type User = {
name: string;
age: number;
};
let good: User = { name: "Alex", age: 10 };
๐ Putting It All Together
Hereโs a real example using EVERYTHING we learned:
// Literal types for status
type Status = "active" | "inactive" | "pending";
// Template literal for IDs
type UserId = `user-${number}`;
// Types to combine
type HasName = { readonly name: string };
type HasEmail = { email: string };
// Intersection: combine both!
type User = HasName & HasEmail & {
id: UserId;
status: Status;
};
// Create a user
const user: User = {
id: "user-123",
name: "Alex",
email: "alex@example.com",
status: "active"
};
// Destructure with types
const { name, status }: User = user;
user.name = "Bob"; // โ readonly!
user.status = "inactive"; // โ
OK
๐ง Key Takeaways
- Union (
|) = Pick ONE from options - Intersection (
&) = Combine ALL properties - Literal = Exact value only
- Template Literal = Build text types from pieces
- readonly = Lock a property
- const = Lock the whole variable
- Destructuring = Unpack with types
- Use specific types over Object/object/{}
Youโre now a Type Composition master! ๐ These building blocks let you create any type you can imagine!