Mastering Advanced TypeScript: Union Types, Narrowing, and Modules
Exploring Union Types, Narrowing, and Modules in TypeScript
Welcome, TypeScript adventurers! 🚀 In our previous lesson, we conquered the Third Boss by creating a RPG Battle System. Now, it's time to level up our skills with some advanced TypeScript features. Grab your coding swords, and let's dive in!
Union Types: The Power of OR
TypeScript's union types are like Swiss Army knives – versatile and powerful. They allow us to combine two or more types, giving our variables more flexibility.
let pauseGame: boolean | string;
pauseGame = true; // ✅ Valid
pauseGame = 'no'; // ✅ Also valid
pauseGame = 42; // ❌ Error! Not a boolean or string
We can even use union types with custom types or interfaces:
type Diamonds = {};
type Spades = {};
type Hearts = {};
type Clubs = {};
let cards: Diamonds | Spades | Hearts | Clubs;
Narrowing: Refining Our Types
But what happens when we need to work with specific members of a union? That's where narrowing comes in. It's like a magical lens that helps TypeScript focus on a specific type within a union.
Let's imagine we're creating a game with different enemy types:
class Enemy1 {
readonly armour = "heavy";
readonly behaviour = "aggressive";
rush() { /* Rush player */ }
}
class Enemy2 {
readonly armour = "medium";
readonly behaviour = "standard";
fight() { /* Fight player */ }
}
function executeBehaviour(enemy: Enemy1 | Enemy2 | Enemy3 | Enemy4) {
switch (enemy.behaviour) {
case 'aggressive':
enemy.rush();
break;
case 'standard':
enemy.fight();
break;
}
}
By using a switch statement on the behaviour property, TypeScript can narrow down the enemy type and allow us to call the correct method.
Property Modifiers: Optional and Readonly
TypeScript gives us fine-grained control over our object properties with modifiers:
Optional Properties
Use the ?
symbol to make a property optional:
type Player = {
name: string,
health: number,
class: string,
mana?: number, // Optional property
isAlive: boolean
}
let warrior: Player = {
name: 'Conan',
health: 100,
class: 'warrior',
isAlive: true
// No mana needed for warriors!
}
Readonly Properties
The readonly keyword prevents properties from being changed after initialization:
class ImmutableEnemy {
readonly name: string;
constructor(name: string) {
this.name = name;
}
}
const boss = new ImmutableEnemy("Final Boss");
boss.name = "Not So Final Boss"; // ❌ Error! Can't modify readonly property
Modules: Organizing Our Code
As our projects grow, we need ways to organize and reuse our code. That's where modules come in!
Exporting and Importing
Let's say we have a file weapons.ts
:
export type Weapon = { name: string, damage: number };
export function createSword(name: string): Weapon {
return { name, damage: 10 };
}
export default function createAxe(name: string): Weapon {
return { name, damage: 12 };
}
We can use these in another file:
import createAxe, { Weapon, createSword } from './weapons';
const mySword: Weapon = createSword("Excalibur");
const myAxe: Weapon = createAxe("Leviathan");
Module Systems
TypeScript supports different module systems. The two most common are ES Modules and CommonJS. You can specify which one to use in your tsconfig.json
:
{
"compilerOptions": {
"module": "ES6", // or "CommonJS" for older Node.js versions
}
}
This is particularly useful when working with platforms that have specific JavaScript requirements, like AWS Lambda.
Wrapping Up
Congratulations, TypeScript warriors! 🎉 You've just leveled up your TypeScript skills with union types, narrowing, property modifiers, and modules. These tools will help you write more flexible, maintainable, and powerful code.
Remember, the key to mastering these concepts is practice. Try incorporating them into your next project and see how they can improve your code!
What advanced TypeScript feature are you most excited to use? Let me know in the comments below!
Happy coding, and see you in the next level of our TypeScript adventure! 🚀