Prerequisites
- Basic JavaScript knowledge ๐
- TypeScript installed โก
- Understanding of variables ๐ป
What you'll learn
- Master all TypeScript primitive types ๐ฏ
- Work with arrays, tuples, and enums ๐๏ธ
- Understand type inference and annotations ๐
- Apply basic types in real projects โจ
๐ฏ Introduction
Welcome to the colorful world of TypeScript types! ๐ If variables are the building blocks of your code, types are the blueprints that keep everything sturdy and safe.
Think of TypeScript types like labels on containers ๐ฆ. Just as you wouldnโt put orange juice in a container labeled โmilkโ (chaos!), TypeScript ensures your variables hold the right kind of data. This prevents bugs, improves code clarity, and makes your development experience delightful!
By the end of this tutorial, youโll be a TypeScript types wizard, ready to build type-safe applications! Letโs explore! ๐โโ๏ธ
๐ Understanding TypeScript Types
๐ค What Are Types?
Types are like DNA for your data ๐งฌ. They define what kind of information a variable can hold and what operations you can perform on it.
JavaScript is dynamically typed (types are checked at runtime), but TypeScript adds static typing (types are checked at compile time). This means:
- โจ Catch errors before running your code
- ๐ Get intelligent code completion
- ๐ก๏ธ Refactor with confidence
- ๐ Self-documenting code
๐ก Type Annotations vs Inference
TypeScript is smart! It can often figure out types automatically:
// ๐ฏ Type Inference - TypeScript figures it out
let message = "Hello TypeScript!"; // TypeScript knows it's a string
let count = 42; // TypeScript knows it's a number
// ๐ Type Annotations - You explicitly declare the type
let greeting: string = "Welcome!";
let age: number = 25;
// ๐ก When to use each?
// Use inference when it's obvious
let obvious = "I'm clearly a string!";
// Use annotations when it's not clear
let notObvious: string; // Will be assigned later
notObvious = getUserInput();
๐ง Primitive Types
๐ String Type
Strings are for text - your appโs words and sentences:
// ๐จ String basics
let firstName: string = "Sarah";
let lastName: string = 'Connor';
let fullName: string = `${firstName} ${lastName}`; // Template literals work too!
// ๐ฏ String operations
let message: string = "TypeScript is awesome!";
let shout: string = message.toUpperCase(); // "TYPESCRIPT IS AWESOME!"
let length: number = message.length; // 22
// ๐ก Real-world example
function createWelcomeMessage(name: string): string {
return `Welcome aboard, ${name}! ๐`;
}
// โ
Works perfectly
console.log(createWelcomeMessage("Alex")); // "Welcome aboard, Alex! ๐"
// โ TypeScript prevents errors!
// createWelcomeMessage(123); // Error: Argument of type 'number' is not assignable
๐ข Number Type
Numbers handle all numeric values - integers and decimals:
// ๐ฏ Number basics
let age: number = 25;
let price: number = 19.99;
let temperature: number = -5;
let billion: number = 1_000_000_000; // Underscores for readability!
// ๐งฎ Math operations
let sum: number = 10 + 5; // 15
let product: number = 4 * 7; // 28
let division: number = 20 / 4; // 5
let remainder: number = 10 % 3; // 1
// ๐ฐ Real-world example: Shopping cart
function calculateTotal(items: number[], taxRate: number): number {
const subtotal = items.reduce((sum, price) => sum + price, 0);
const tax = subtotal * taxRate;
return subtotal + tax;
}
const cart: number[] = [29.99, 15.50, 7.99];
const total = calculateTotal(cart, 0.08); // $57.58 with 8% tax
console.log(`Total: $${total.toFixed(2)} ๐ณ`);
โ Boolean Type
Booleans are simple but powerful - true or false:
// ๐ฏ Boolean basics
let isActive: boolean = true;
let isComplete: boolean = false;
let hasPermission: boolean = true;
// ๐ง Boolean logic
let canEdit: boolean = isActive && hasPermission; // true
let needsAttention: boolean = !isComplete || isActive; // true
// ๐ฎ Real-world example: Game state
interface GameState {
isPlaying: boolean;
isPaused: boolean;
isGameOver: boolean;
hasHighScore: boolean;
}
function updateGameState(state: GameState, score: number): GameState {
return {
...state,
isGameOver: score <= 0,
hasHighScore: score > 1000,
isPlaying: !state.isPaused && !state.isGameOver
};
}
// ๐ฏ Usage
let gameState: GameState = {
isPlaying: true,
isPaused: false,
isGameOver: false,
hasHighScore: false
};
gameState = updateGameState(gameState, 1500);
console.log(gameState.hasHighScore ? "New high score! ๐" : "Keep trying! ๐ช");
๐ก Special Types
๐ Null and Undefined
These represent absence of value, but theyโre different:
// ๐ Understanding null and undefined
let notYetAssigned: undefined = undefined; // Variable declared but not assigned
let explicitlyEmpty: null = null; // Intentionally empty
// ๐ก Real-world usage with union types
let userEmail: string | null = null; // Email might not exist
function findUser(id: number): User | null {
const user = database.find(u => u.id === id);
return user || null; // Explicitly return null if not found
}
// ๐ก๏ธ Safe handling with strictNullChecks
function greetUser(name: string | undefined): string {
// โ Unsafe - could error if name is undefined
// return `Hello, ${name.toUpperCase()}!`;
// โ
Safe - check first!
if (name !== undefined) {
return `Hello, ${name.toUpperCase()}! ๐`;
}
return "Hello, stranger! ๐";
}
// ๐ฏ Optional parameters use undefined
function createProduct(name: string, description?: string): Product {
return {
name,
description: description || "No description available"
};
}
๐ซ Void Type
Void represents the absence of a return value:
// ๐ฏ Functions that don't return anything
function logMessage(message: string): void {
console.log(`๐ข ${message}`);
// No return statement!
}
// ๐จ Event handlers often return void
function handleClick(event: MouseEvent): void {
console.log("Button clicked! ๐ฑ๏ธ");
updateUI();
trackEvent("button_click");
// Performs actions but doesn't return a value
}
// โ ๏ธ Void vs undefined
function returnsUndefined(): undefined {
return undefined; // Must explicitly return undefined
}
function returnsVoid(): void {
// Can return nothing or return; (empty return)
return;
}
๐ญ Any Type (Use Sparingly!)
Any disables type checking - use it as a last resort:
// โ ๏ธ Any type - TypeScript gives up!
let mystery: any = 42;
mystery = "now I'm a string";
mystery = { weird: true };
mystery.nonExistent.method(); // No error at compile time! ๐ฅ
// ๐ก๏ธ Better alternatives to any
// 1. Unknown - safer any
let saferMystery: unknown = getData();
if (typeof saferMystery === 'string') {
console.log(saferMystery.toUpperCase()); // Must check type first
}
// 2. Union types - be specific
let flexible: string | number = "hello";
flexible = 42; // Still type-safe!
// 3. Generics - maintain type safety
function processData<T>(data: T): T {
console.log("Processing:", data);
return data;
}
// ๐ When any might be acceptable (rarely!)
// Migrating JavaScript code gradually
const legacyFunction: any = window.legacyLibrary; // Third-party code
๐ Array and Tuple Types
๐ Arrays
Arrays store multiple values of the same type:
// ๐ฏ Array syntax - two ways
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ["Alice", "Bob", "Charlie"];
// ๐จ Working with arrays
let fruits: string[] = ["apple", "banana", "orange"];
fruits.push("mango"); // โ
Adding strings is fine
// fruits.push(123); // โ Error: can't add numbers!
// ๐ Real-world example: Shopping list
interface Item {
name: string;
quantity: number;
bought: boolean;
}
let shoppingList: Item[] = [
{ name: "Milk ๐ฅ", quantity: 2, bought: false },
{ name: "Bread ๐", quantity: 1, bought: true },
{ name: "Eggs ๐ฅ", quantity: 12, bought: false }
];
// ๐ฏ Array methods with types
const unboughtItems: Item[] = shoppingList.filter(item => !item.bought);
const itemNames: string[] = shoppingList.map(item => item.name);
const totalQuantity: number = shoppingList.reduce(
(sum, item) => sum + item.quantity,
0
);
// ๐ฎ Multi-dimensional arrays
let gameBoard: string[][] = [
["X", "O", "X"],
["O", "X", "O"],
["X", "O", "X"]
];
๐ฏ Tuples
Tuples are fixed-length arrays with specific types at each position:
// ๐ Tuple basics - order and types matter!
let coordinate: [number, number] = [10, 20];
let user: [string, number, boolean] = ["Alice", 25, true];
// ๐จ Named tuples (TypeScript 4.0+)
type Point3D = [x: number, y: number, z: number];
let position: Point3D = [10, 20, 30];
// ๐ Real-world example: RGB colors
type RGB = [red: number, green: number, blue: number];
let primaryRed: RGB = [255, 0, 0];
let oceanBlue: RGB = [0, 119, 190];
function mixColors(color1: RGB, color2: RGB): RGB {
return [
Math.round((color1[0] + color2[0]) / 2),
Math.round((color1[1] + color2[1]) / 2),
Math.round((color1[2] + color2[2]) / 2)
];
}
// ๐ Database query results
type QueryResult = [id: number, name: string, email: string];
let userRecord: QueryResult = [1, "John Doe", "[email protected]"];
// ๐ฏ Destructuring tuples
let [id, name, email] = userRecord;
console.log(`User ${name} (${email}) has ID: ${id}`);
// โ ๏ธ Tuples vs Arrays
let flexibleArray: number[] = [1, 2, 3, 4, 5]; // Can have any length
let strictTuple: [number, number] = [1, 2]; // Must have exactly 2 elements
๐จ Enum Type
Enums define a set of named constants:
// ๐ฏ Numeric enums
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
let playerDirection: Direction = Direction.Up;
// ๐ฎ Game example with custom values
enum GameStatus {
NotStarted = 0,
InProgress = 1,
Paused = 2,
GameOver = 3,
Victory = 4
}
function handleGameStatus(status: GameStatus): string {
switch (status) {
case GameStatus.NotStarted:
return "Press START to begin! ๐ฎ";
case GameStatus.InProgress:
return "Game on! ๐โโ๏ธ";
case GameStatus.Paused:
return "Paused - Press P to continue โธ๏ธ";
case GameStatus.GameOver:
return "Game Over! Try again? ๐";
case GameStatus.Victory:
return "You win! Congratulations! ๐";
}
}
// ๐จ String enums
enum Color {
Red = "#FF0000",
Green = "#00FF00",
Blue = "#0000FF",
Purple = "#800080"
}
let favoriteColor: Color = Color.Purple;
console.log(`My favorite color is ${favoriteColor}`); // "#800080"
// ๐ Real-world example: HTTP status codes
enum HttpStatus {
OK = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
ServerError = 500
}
function handleResponse(status: HttpStatus): void {
if (status === HttpStatus.OK) {
console.log("Success! โ
");
} else if (status >= 400) {
console.log("Error occurred! โ");
}
}
// ๐ฏ Const enums (more efficient)
const enum Size {
Small = "S",
Medium = "M",
Large = "L",
ExtraLarge = "XL"
}
let shirtSize: Size = Size.Medium;
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Type Assertion Abuse
// โ Dangerous type assertion
let someValue: any = "This is a string";
let strLength: number = (someValue as string).length; // Works but risky!
// What if someValue changes?
someValue = 123;
// strLength = (someValue as string).length; // ๐ฅ Runtime error!
// โ
Safe approach - Type guards
function getLength(value: unknown): number {
if (typeof value === 'string') {
return value.length;
}
if (Array.isArray(value)) {
return value.length;
}
return 0;
}
๐คฏ Pitfall 2: Array Type Confusion
// โ Common mistake - wrong syntax
// let numbers: number = [1, 2, 3]; // Error!
// let names: [string] = ["Alice", "Bob"]; // This is a tuple with one element!
// โ
Correct array syntax
let numbers: number[] = [1, 2, 3];
let names: string[] = ["Alice", "Bob"];
// โ Mixing types without union
// let mixed: string[] = ["hello", 42]; // Error!
// โ
Use union types for mixed arrays
let mixed: (string | number)[] = ["hello", 42, "world", 100];
๐ต Pitfall 3: Null/Undefined Confusion
// โ Not handling null/undefined
function getUser(id: number): User | null {
return database.find(user => user.id === id) || null;
}
let user = getUser(123);
// console.log(user.name); // ๐ฅ Object is possibly 'null'
// โ
Proper null checking
if (user !== null) {
console.log(user.name); // Safe!
}
// โ
Or use optional chaining
console.log(user?.name || "User not found");
๐ ๏ธ Best Practices
๐ฏ Type Best Practices
-
๐ท๏ธ Always Use Specific Types: Be as specific as possible
// โ Too generic let data: any[] = [1, 2, 3]; // โ Specific let scores: number[] = [1, 2, 3];
-
๐ Let TypeScript Infer When Obvious: Donโt over-annotate
// โ Redundant let name: string = "Alice"; // โ Clean let name = "Alice"; // TypeScript knows it's a string
-
๐ก๏ธ Enable Strict Mode: Maximum type safety
{ "compilerOptions": { "strict": true, "strictNullChecks": true } }
-
๐จ Use Enums for Fixed Sets: Better than magic strings
// โ Magic strings if (status === "active") { } // โ Type-safe enum if (status === UserStatus.Active) { }
-
โจ Prefer Unknown Over Any: Safer for unknown types
// โ Dangerous function process(data: any) { } // โ Safe function process(data: unknown) { }
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Type-Safe Task Manager
Create a task management system using all the basic types:
๐ Requirements:
- โ Use all primitive types (string, number, boolean)
- ๐ท๏ธ Create enums for task status and priority
- ๐ฅ Use arrays for task lists
- ๐ Handle null/undefined for optional fields
- ๐จ Use tuples for task metadata
๐ Bonus Points:
- Implement type-safe filtering
- Add date handling
- Create summary statistics
๐ก Solution
๐ Click to see solution
// ๐ฏ Enums for task properties
enum TaskStatus {
Todo = "TODO",
InProgress = "IN_PROGRESS",
Done = "DONE",
Archived = "ARCHIVED"
}
enum TaskPriority {
Low = 1,
Medium = 2,
High = 3,
Urgent = 4
}
// ๐ Task interface using all basic types
interface Task {
id: number; // number
title: string; // string
description: string | null; // string or null
isCompleted: boolean; // boolean
priority: TaskPriority; // enum
status: TaskStatus; // enum
tags: string[]; // array
assignee?: string; // optional (undefined)
dueDate: Date | null; // Date or null
metadata: [created: Date, modified: Date]; // tuple
}
// ๐๏ธ Task Manager class
class TaskManager {
private tasks: Task[] = [];
private nextId: number = 1;
// โ Add new task
addTask(
title: string,
priority: TaskPriority = TaskPriority.Medium
): Task {
const now = new Date();
const newTask: Task = {
id: this.nextId++,
title,
description: null,
isCompleted: false,
priority,
status: TaskStatus.Todo,
tags: [],
dueDate: null,
metadata: [now, now]
};
this.tasks.push(newTask);
console.log(`โ
Task added: ${title}`);
return newTask;
}
// ๐ Find tasks by status
getTasksByStatus(status: TaskStatus): Task[] {
return this.tasks.filter(task => task.status === status);
}
// ๐ Get statistics
getStats(): Record<string, number> {
const stats = {
total: this.tasks.length,
completed: 0,
inProgress: 0,
highPriority: 0
};
this.tasks.forEach(task => {
if (task.isCompleted) stats.completed++;
if (task.status === TaskStatus.InProgress) stats.inProgress++;
if (task.priority >= TaskPriority.High) stats.highPriority++;
});
return stats;
}
// ๐จ Update task
updateTask(id: number, updates: Partial<Task>): void {
const taskIndex = this.tasks.findIndex(t => t.id === id);
if (taskIndex !== -1) {
const task = this.tasks[taskIndex];
this.tasks[taskIndex] = {
...task,
...updates,
metadata: [task.metadata[0], new Date()] // Update modified date
};
console.log(`๐ Task ${id} updated`);
}
}
}
// ๐ฎ Usage example
const taskManager = new TaskManager();
// Add tasks
const task1 = taskManager.addTask("Learn TypeScript basics ๐", TaskPriority.High);
const task2 = taskManager.addTask("Build a project ๐๏ธ", TaskPriority.Medium);
const task3 = taskManager.addTask("Review and practice ๐", TaskPriority.Low);
// Update task
taskManager.updateTask(task1.id, {
status: TaskStatus.InProgress,
description: "Master all primitive types!",
tags: ["learning", "typescript", "fundamentals"]
});
// Get statistics
const stats = taskManager.getStats();
console.log("๐ Task Statistics:", stats);
// Filter tasks
const inProgressTasks = taskManager.getTasksByStatus(TaskStatus.InProgress);
console.log(`๐ง Tasks in progress: ${inProgressTasks.length}`);
๐ Key Takeaways
Youโve mastered TypeScript basic types! Hereโs what you can now do:
- โ Use all primitive types with confidence ๐ช
- โ Work with arrays and tuples effectively ๐ฏ
- โ Create and use enums for better code organization ๐ท๏ธ
- โ Handle null and undefined safely ๐ก๏ธ
- โ Choose the right type for every situation! ๐
Remember: Types are your friends - they catch bugs, document code, and make development more enjoyable! ๐ค
๐ค Next Steps
Congratulations! ๐ Youโve built a solid foundation in TypeScript types!
Hereโs what to do next:
- ๐ป Practice with the exercise above
- ๐๏ธ Add types to an existing JavaScript project
- ๐ Learn about interfaces and type aliases
- ๐ Explore advanced types like unions and generics!
Remember: Every TypeScript expert started with these basics. Keep practicing! ๐
Happy coding! ๐๐โจ