- Published on
Part 2: Interfaces and Type Aliases
- Authors
- Name
- Diego Herrera Redondo
- @diegxherrera
Welcome back to the TypeScript Crash Course! 🎉 In Part 1, we explored TypeScript’s basic type annotations and saw how they make code more readable and reliable. In Part 2, we’re diving deeper by exploring interfaces and type aliases. These features allow us to define complex types, making it easy to structure and organize data in TypeScript. Ready to enhance your TypeScript skills? Let’s go! 🚀
Interfaces in TypeScript 🎨
An interface in TypeScript is a way to define the structure of an object. Interfaces are a great way to set expectations for data shapes and improve readability. Let’s look at some examples.
Defining an Interface
Here’s a simple User
interface:
interface User {
name: string;
age: number;
isAdmin: boolean;
}
let user: User = {
name: "Alice",
age: 25,
isAdmin: false
};
In this example, User
specifies that every user
object should have a name
(string), age
(number), and isAdmin
(boolean). TypeScript will alert you if you miss a property or use an incorrect type.
Optional and Readonly Properties 🔐
Interfaces allow you to define optional properties (using ?
) and readonly properties (using readonly
).
Optional Properties
Optional properties aren’t required when creating an object.
interface Book {
title: string;
author: string;
publishedYear?: number; // Optional
}
let myBook: Book = {
title: "TypeScript Handbook",
author: "Microsoft"
};
Readonly Properties
Readonly properties can be set when the object is created, but they can’t be modified afterward.
interface Person {
readonly id: number;
name: string;
}
let person: Person = { id: 1, name: "Bob" };
// person.id = 2; // Error: Cannot assign to 'id' because it is a read-only property
Extending Interfaces 🔄
Interfaces can extend other interfaces, allowing you to create complex structures by reusing properties from other interfaces.
Example
interface Shape {
color: string;
}
interface Circle extends Shape {
radius: number;
}
let circle: Circle = {
color: "red",
radius: 5
};
In this example, Circle
extends Shape
, so it inherits the color
property and adds its own radius
property.
Type Aliases 📝
Type aliases are similar to interfaces but can define more than just object types. They allow us to create custom names for types, including unions, intersections, and functions.
Creating a Basic Type Alias
type ID = string | number;
let userID: ID = "12345"; // Can be a string
userID = 67890; // Or a number
In this example, ID
can be either a string
or a number
, and we use it as a type for userID
.
Type Aliases vs. Interfaces 🤔
While interfaces are often used for object structures, type aliases offer flexibility and can handle unions, intersections, and other complex types. Here’s a comparison:
- Use interfaces when defining the structure of an object.
- Use type aliases for unions, intersections, and other flexible types.
Example: Type Alias with Union Types
type Status = "active" | "inactive" | "suspended";
function updateStatus(status: Status): void {
console.log(`Status updated to ${status}`);
}
updateStatus("active"); // ✅
updateStatus("inactive"); // ✅
// updateStatus("pending"); // ❌ Error
Combining Interfaces and Type Aliases 🧩
You can combine interfaces and type aliases for greater flexibility.
Example: Union of Interfaces
interface Cat {
meow: () => void;
}
interface Dog {
bark: () => void;
}
type Pet = Cat | Dog;
function interactWithPet(pet: Pet): void {
if ("meow" in pet) {
pet.meow();
} else {
pet.bark();
}
}
In this example, Pet
can be either a Cat
or a Dog
. By checking for specific methods, we ensure the correct behavior for each type.
Practical Example: A Product Management System 📦
Let’s apply interfaces and type aliases in a Product Management example.
- Define a
Product
interface withid
,name
,price
, and an optionaldescription
. - Define a
Category
type alias for product categories (e.g., "Electronics", "Books"). - Write a function
addProduct
that takes aProduct
and aCategory
.
Example Solution
interface Product {
id: number;
name: string;
price: number;
description?: string; // Optional
}
type Category = "Electronics" | "Books" | "Clothing";
let products: Product[] = [];
function addProduct(product: Product, category: Category): void {
products.push(product);
console.log(`Added ${product.name} to ${category} category.`);
}
addProduct({ id: 1, name: "Laptop", price: 999 }, "Electronics");
console.log(products);
In this example, Product
defines the structure of each product, and Category
restricts categories to a specific set of values.
Practice Challenge: Define a User System 🎲
Let’s practice by creating a simple User System.
- Create a
User
interface withusername
,email
, and an optionalage
property. - Define a
Role
type alias that can be"admin"
,"editor"
, or"viewer"
. - Write a function
assignRole
that takes aUser
and aRole
and logs a message.
Example Solution
interface User {
username: string;
email: string;
age?: number;
}
type Role = "admin" | "editor" | "viewer";
function assignRole(user: User, role: Role): void {
console.log(`${user.username} is assigned the role of ${role}.`);
}
const newUser: User = { username: "john_doe", email: "john@example.com" };
assignRole(newUser, "admin"); // Output: john_doe is assigned the role of admin.
This example creates a User
object, then assigns it a role using the assignRole
function.
Wrapping Up
In Part 2 of our TypeScript series, we explored interfaces and type aliases to define complex data structures. These tools allow you to create flexible, readable code, making it easier to work with complex data in TypeScript.
In Part 3, we’ll dive into functions and generics in TypeScript, further enhancing your ability to write reusable, robust code. Thanks for following along, and happy coding! 🎉