Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /Typescript
  4. /Interfaces
Your Progress0%
0 of 30 completed

TypeScript Topics

Getting Started

  • What is TypeScript?
  • TypeScript vs JavaScript
  • Setting Up TypeScript
  • Your First TypeScript Program

Basic Types

  • Primitive Types
  • Arrays and Tuples
  • Objects and Type Aliases
  • Enums
  • Any, Unknown, and Never

Functions & Interfaces

  • Function Types and Signatures
  • Interfaces
  • Optional and Default Parameters
  • Function Overloading

Advanced Types

  • Union and Intersection Types
  • Type Guards and Narrowing
  • Literal Types
  • Type Assertions and Casting

Generics

  • Introduction to Generics
  • Generic Constraints
  • Utility Types

Classes & OOP

  • Classes and Constructors
  • Access Modifiers
  • Inheritance and Polymorphism
  • Abstract Classes and Interfaces

Practical TypeScript

  • Working with Modules
  • TypeScript with React
  • Async/Await and Promises
  • Working with APIs and JSON
  • Type Declarations and DefinitelyTyped
  • TypeScript Best Practices

Interfaces

Defining contracts for objects, classes, and functions

Interfaces are one of TypeScript's most powerful features. They define contracts that describe the shape of objects, ensuring that data structures follow a specific format. Think of interfaces as blueprints that specify what properties and methods an object must have. Let's master TypeScript interfaces!

What Are Interfaces?

An interface is a TypeScript construct that defines a contract for object shapes. It specifies what properties an object should have and their types, but doesn't provide implementation.

interface-basics.ts
// Define an interface
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// Create an object that implements the interface
const user: User = {
  id: 1,
  name: "Yusuf Ibrahim",
  email: "yusuf@example.com",
  age: 25
};

// TypeScript checks the shape matches
console.log(`User: ${user.name}, Email: ${user.email}`);

// These cause errors:
// const invalidUser: User = {  // ❌ Missing properties
//   id: 1,
//   name: "Amina"
// };

// const wrongType: User = {    // ❌ Wrong type
//   id: "1",
//   name: "Chidi",
//   email: "chidi@example.com",
//   age: 28
// };

Why Use Interfaces?

  • Contracts: Define what an object must contain
  • Type Safety: Catch errors at compile-time
  • Documentation: Self-documenting code
  • Reusability: Use the same interface in multiple places
  • Refactoring: Easy to update structure everywhere
  • Extensibility: Can extend and merge interfaces

Basic Interface Syntax

interface-syntax.ts
// Simple interface
interface Product {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
}

// Using the interface
const laptop: Product = {
  id: 1,
  name: "Dell XPS 15",
  price: 450000,
  inStock: true
};

// Interface with methods
interface Calculator {
  add(a: number, b: number): number;
  subtract(a: number, b: number): number;
  multiply(a: number, b: number): number;
  divide(a: number, b: number): number;
}

// Implementing the interface
const calculator: Calculator = {
  add(a, b) {
    return a + b;
  },
  subtract(a, b) {
    return a - b;
  },
  multiply(a, b) {
    return a * b;
  },
  divide(a, b) {
    if (b === 0) throw new Error("Cannot divide by zero");
    return a / b;
  }
};

console.log(calculator.add(10, 5));       // 15
console.log(calculator.multiply(10, 5));  // 50

User Interface Structure

▼
Userinterface

User profile interface with extensions

interface User {
  id: number;
  name: string;
  email: string;
  readonly createdAt: Date;
  profilePicture?: string;
}
idnumber

Unique user identifier (required)

namestring

User's full name (required)

emailstring

User's email address (required)

createdAtDate (readonly)

Account creation date (cannot be modified)

profilePicturestring | undefined

Profile picture URL (optional)

Optional Properties

Use ? to make properties optional. Optional properties may or may not be present.

optional-properties.ts
// Interface with optional properties
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;              // Optional
  phone?: string;            // Optional
  address?: string;          // Optional
}

// Valid with only required properties
const user1: User = {
  id: 1,
  name: "Yusuf Ibrahim",
  email: "yusuf@example.com"
};

// Valid with some optional properties
const user2: User = {
  id: 2,
  name: "Amina Hassan",
  email: "amina@example.com",
  age: 30,
  phone: "+234-803-123-4567"
};

// Valid with all properties
const user3: User = {
  id: 3,
  name: "Chidi Okafor",
  email: "chidi@example.com",
  age: 28,
  phone: "+234-805-987-6543",
  address: "Lagos, Africa"
};

// Function handling optional properties
function displayUser(user: User): void {
  console.log(`Name: ${user.name}`);
  console.log(`Email: ${user.email}`);
  
  if (user.age !== undefined) {
    console.log(`Age: ${user.age}`);
  }
  
  console.log(`Phone: ${user.phone ?? "Not provided"}`);
  console.log(`Address: ${user.address ?? "Not provided"}`);
}

displayUser(user1);
displayUser(user2);

Readonly Properties

Use readonly to prevent properties from being modified after object creation.

readonly-properties.ts
// Interface with readonly properties
interface User {
  readonly id: number;           // Cannot be changed
  readonly createdAt: Date;      // Cannot be changed
  name: string;                  // Can be changed
  email: string;                 // Can be changed
}

const user: User = {
  id: 1,
  createdAt: new Date(),
  name: "Yusuf Ibrahim",
  email: "yusuf@example.com"
};

// These are allowed
user.name = "Yusuf A. Ibrahim";
user.email = "yusuf.new@example.com";

// These cause errors
// user.id = 2;                  // ❌ Error: Cannot assign to 'id'
// user.createdAt = new Date();  // ❌ Error: Cannot assign to 'createdAt'

console.log(`User ID: ${user.id} (readonly)`);
console.log(`Name: ${user.name} (can be changed)`);

// Readonly with nested objects
interface Article {
  readonly id: number;
  readonly author: {
    readonly name: string;
    readonly email: string;
  };
  title: string;
  content: string;
}

const article: Article = {
  id: 1,
  author: {
    name: "Yusuf Ibrahim",
    email: "yusuf@example.com"
  },
  title: "TypeScript Guide",
  content: "Learning TypeScript..."
};

// Can modify non-readonly properties
article.title = "Complete TypeScript Guide";

// Cannot modify readonly properties
// article.id = 2;                    // ❌ Error
// article.author.name = "New Name";  // ❌ Error

Extending Interfaces

Interfaces can extend other interfaces, inheriting their properties. This promotes code reuse and creates hierarchies.

extending-interfaces.ts
// Base interface
interface Person {
  name: string;
  age: number;
}

// Extended interface
interface Employee extends Person {
  employeeId: number;
  department: string;
  salary: number;
}

// Employee has all Person properties plus its own
const employee: Employee = {
  name: "Yusuf Ibrahim",
  age: 25,
  employeeId: 1001,
  department: "Engineering",
  salary: 500000
};

console.log(`Employee: ${employee.name}`);
console.log(`Department: ${employee.department}`);

// Extend multiple interfaces
interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface Identifiable {
  id: number;
}

interface User extends Person, Timestamped, Identifiable {
  email: string;
}

const user: User = {
  id: 1,
  name: "Amina Hassan",
  age: 30,
  email: "amina@example.com",
  createdAt: new Date("2024-01-01"),
  updatedAt: new Date("2024-12-25")
};

console.log(`User: ${user.name}, ID: ${user.id}`);

Extending with Additional Properties

extending-additional.ts
// Base product interface
interface Product {
  id: number;
  name: string;
  price: number;
}

// Extended for physical products
interface PhysicalProduct extends Product {
  weight: number;
  dimensions: {
    length: number;
    width: number;
    height: number;
  };
  shippingCost: number;
}

// Extended for digital products
interface DigitalProduct extends Product {
  downloadUrl: string;
  fileSize: number;
  format: string;
}

const laptop: PhysicalProduct = {
  id: 1,
  name: "Dell Laptop",
  price: 450000,
  weight: 2.5,
  dimensions: {
    length: 35,
    width: 25,
    height: 2
  },
  shippingCost: 5000
};

const ebook: DigitalProduct = {
  id: 2,
  name: "TypeScript Guide",
  price: 5000,
  downloadUrl: "https://example.com/download/typescript.pdf",
  fileSize: 2048576,
  format: "PDF"
};

console.log(`Physical: ${laptop.name}, Weight: ${laptop.weight}kg`);
console.log(`Digital: ${ebook.name}, Size: ${ebook.fileSize} bytes`);

Interfaces for Functions

Interfaces can describe function signatures, not just object shapes.

function-interfaces.ts
// Interface for a function
interface MathOperation {
  (a: number, b: number): number;
}

// Implement the interface
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
const multiply: MathOperation = (a, b) => a * b;

console.log(add(10, 5));       // 15
console.log(subtract(10, 5));  // 5
console.log(multiply(10, 5));  // 50

// Interface with properties and methods
interface StringFormatter {
  format(str: string): string;
  maxLength: number;
}

const upperCaseFormatter: StringFormatter = {
  maxLength: 50,
  format(str: string): string {
    return str.toUpperCase().slice(0, this.maxLength);
  }
};

console.log(upperCaseFormatter.format("hello world"));  // "HELLO WORLD"

// Callable interface with properties
interface Logger {
  (message: string): void;
  level: string;
  enabled: boolean;
}

const logger = ((message: string) => {
  if (logger.enabled) {
    console.log(`[${logger.level}] ${message}`);
  }
}) as Logger;

logger.level = "INFO";
logger.enabled = true;

logger("Application started");  // "[INFO] Application started"

Indexable Interfaces

Interfaces can describe objects with dynamic property names using index signatures.

indexable-interfaces.ts
// Interface with index signature
interface Dictionary {
  [key: string]: string;
}

const translations: Dictionary = {
  hello: "Sannu",
  goodbye: "Sai anjima",
  thank_you: "Na gode",
  yes: "Eh",
  no: "A'a"
};

console.log(translations.hello);      // "Sannu"
console.log(translations["goodbye"]); // "Sai anjima"

// Add new properties dynamically
translations.please = "Don Allah";
translations.welcome = "Barka da zuwa";

// Index signature with number keys
interface NumberDictionary {
  [index: number]: string;
}

const weekDays: NumberDictionary = {
  0: "Sunday",
  1: "Monday",
  2: "Tuesday",
  3: "Wednesday",
  4: "Thursday",
  5: "Friday",
  6: "Saturday"
};

console.log(weekDays[0]);  // "Sunday"
console.log(weekDays[5]);  // "Friday"

// Combine known properties with index signature
interface Config {
  host: string;
  port: number;
  [key: string]: string | number;  // Additional properties
}

const serverConfig: Config = {
  host: "localhost",
  port: 8080,
  timeout: 30000,
  maxConnections: 100,
  environment: "development"
};

console.log(serverConfig.host);      // "localhost"
console.log(serverConfig.timeout);   // 30000

Implementing Interfaces in Classes

Classes can implement interfaces, ensuring they provide all required properties and methods.

class-implementation.ts
// Interface definition
interface Printable {
  print(): void;
}

interface Saveable {
  save(): void;
  load(): void;
}

// Class implementing single interface
class Document implements Printable {
  constructor(public content: string) {}

  print(): void {
    console.log("Printing:", this.content);
  }
}

// Class implementing multiple interfaces
class Report implements Printable, Saveable {
  constructor(public title: string, public data: string) {}

  print(): void {
    console.log(`Report: ${this.title}`);
    console.log(this.data);
  }

  save(): void {
    console.log(`Saving report: ${this.title}`);
  }

  load(): void {
    console.log(`Loading report: ${this.title}`);
  }
}

const doc = new Document("Hello World");
doc.print();  // "Printing: Hello World"

const report = new Report("Q4 Sales", "Sales data...");
report.print();  // "Report: Q4 Sales" + data
report.save();   // "Saving report: Q4 Sales"

// Interface with properties and methods
interface User {
  id: number;
  name: string;
  email: string;
  displayInfo(): void;
}

class Customer implements User {
  constructor(
    public id: number,
    public name: string,
    public email: string,
    public orderCount: number = 0
  ) {}

  displayInfo(): void {
    console.log(`Customer: ${this.name}`);
    console.log(`Email: ${this.email}`);
    console.log(`Orders: ${this.orderCount}`);
  }

  placeOrder(): void {
    this.orderCount++;
    console.log(`Order placed. Total orders: ${this.orderCount}`);
  }
}

const customer = new Customer(1, "Yusuf Ibrahim", "yusuf@example.com");
customer.displayInfo();
customer.placeOrder();

Declaration Merging

Unlike type aliases, interfaces with the same name automatically merge their declarations. This is useful for extending third-party library types.

declaration-merging.ts
// First declaration
interface User {
  id: number;
  name: string;
}

// Second declaration - merges with first
interface User {
  email: string;
}

// Third declaration - merges with previous
interface User {
  age?: number;
}

// Result: User has all properties from all declarations
const user: User = {
  id: 1,
  name: "Yusuf Ibrahim",
  email: "yusuf@example.com",
  age: 25
};

console.log(user);

// Practical example: Extending Window interface
interface Window {
  myCustomProperty: string;
}

// Now TypeScript knows about this property
// window.myCustomProperty = "Custom value";

// Extending library types
interface Array<T> {
  customMethod(): T[];
}

// Add implementation separately (this is just for type)
// Array.prototype.customMethod = function() { return this; };

Declaration Merging: This is unique to interfaces. Type aliases cannot be declared multiple times—they will cause an error. Use this feature to extend existing types from libraries.

Interface vs Type Alias

Both interfaces and type aliases can define object shapes, but they have important differences.

FeatureInterfaceType Alias
Extend other types✅ Yes (extends)✅ Yes (intersection &)
Declaration merging✅ Yes❌ No
Implement in class✅ Yes✅ Yes
Union types❌ No✅ Yes
Primitive types❌ No✅ Yes
Tuple types❌ No✅ Yes
Best forObject shapes, APIs, classesUnions, primitives, utility types
interface-vs-type.ts
// Both can define objects
interface UserInterface {
  name: string;
  age: number;
}

type UserType = {
  name: string;
  age: number;
};

// Interfaces can extend
interface Employee extends UserInterface {
  employeeId: number;
}

// Types use intersection
type EmployeeType = UserType & {
  employeeId: number;
};

// Only types can do unions
type ID = string | number;  // ✅ Works
// interface ID = string | number;  // ❌ Error

// Only types can alias primitives
type Name = string;  // ✅ Works
// interface Name = string;  // ❌ Error

// Only types can define tuples
type Point = [number, number];  // ✅ Works
// interface Point = [number, number];  // ❌ Error

// Interfaces can merge
interface Window {
  title: string;
}

interface Window {
  version: number;
}
// Window now has both properties

// Types cannot merge
// type Config = { host: string; };
// type Config = { port: number; };  // ❌ Error: Duplicate identifier

💡 When to Use Which?

  • Use Interface: For object shapes, class contracts, and public APIs
  • Use Type: For unions, intersections, primitives, tuples, and utility types
  • General Rule: Default to interfaces for objects, use types for everything else

Practical Examples

Example 1: E-Commerce System

ecommerce-interfaces.ts
// Base interfaces
interface Identifiable {
  id: number;
}

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

// Product interface
interface Product extends Identifiable, Timestamped {
  name: string;
  description: string;
  price: number;
  category: string;
  inStock: boolean;
}

// Customer interface
interface Customer extends Identifiable, Timestamped {
  name: string;
  email: string;
  phone?: string;
  address?: Address;
}

interface Address {
  street: string;
  city: string;
  state: string;
  zipCode: string;
}

// Order interface
interface Order extends Identifiable, Timestamped {
  customerId: number;
  items: OrderItem[];
  totalAmount: number;
  status: OrderStatus;
  shippingAddress: Address;
}

interface OrderItem {
  productId: number;
  quantity: number;
  price: number;
}

type OrderStatus = "pending" | "processing" | "shipped" | "delivered";

// Sample data
const product: Product = {
  id: 1,
  name: "Dell Laptop",
  description: "High-performance laptop",
  price: 450000,
  category: "Electronics",
  inStock: true,
  createdAt: new Date("2024-01-01"),
  updatedAt: new Date("2024-12-25")
};

const customer: Customer = {
  id: 101,
  name: "Yusuf Ibrahim",
  email: "yusuf@example.com",
  phone: "+234-803-123-4567",
  address: {
    street: "123 Main Street",
    city: "Lagos",
    state: "Lagos State",
    zipCode: "100001"
  },
  createdAt: new Date("2024-06-01"),
  updatedAt: new Date("2024-12-25")
};

const order: Order = {
  id: 1001,
  customerId: customer.id,
  items: [
    { productId: product.id, quantity: 1, price: product.price }
  ],
  totalAmount: product.price,
  status: "pending",
  shippingAddress: customer.address!,
  createdAt: new Date(),
  updatedAt: new Date()
};

console.log(`Order ${order.id} for ${customer.name}: ₦${order.totalAmount}`);

Example 2: API Response Handler

api-interfaces.ts
// Base API response interface
interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: ApiError;
  metadata: ResponseMetadata;
}

interface ApiError {
  code: string;
  message: string;
  details?: unknown;
}

interface ResponseMetadata {
  timestamp: Date;
  requestId: string;
  duration: number;
}

// Specific response types
interface UserResponse {
  id: number;
  name: string;
  email: string;
}

interface ProductListResponse {
  products: Product[];
  total: number;
  page: number;
  pageSize: number;
}

interface Product {
  id: number;
  name: string;
  price: number;
}

// Type-safe API responses
const userResponse: ApiResponse<UserResponse> = {
  success: true,
  data: {
    id: 1,
    name: "Yusuf Ibrahim",
    email: "yusuf@example.com"
  },
  metadata: {
    timestamp: new Date(),
    requestId: "req-123",
    duration: 150
  }
};

const errorResponse: ApiResponse<never> = {
  success: false,
  error: {
    code: "USER_NOT_FOUND",
    message: "User with given ID not found"
  },
  metadata: {
    timestamp: new Date(),
    requestId: "req-124",
    duration: 50
  }
};

// Generic handler function
function handleResponse<T>(response: ApiResponse<T>): T {
  if (!response.success) {
    throw new Error(`API Error: ${response.error?.message}`);
  }
  
  if (!response.data) {
    throw new Error("No data in response");
  }
  
  return response.data;
}

const userData = handleResponse(userResponse);
console.log(`User: ${userData.name}`);

Interactive Interface Builder

Build Your Own Interface

id: number
name: string

Add Property

Generated Interface

interface User {
  id: number;
  name: string;
}

Usage Example

const example: User = {
  id: 123,
  name: "example"
};

Test Your Knowledge

Type Safety Check

Which interface usage is correct?

interface User {
  name: string;
  age: number;
}

// Option A
const user1: User = { name: "Yusuf" };

// Option B
const user2: User = { name: "Amina", age: 30 };

// Option C
const user3: User = { name: "Chidi", age: 28, email: "chidi@example.com" };

// Option D
interface User {
  email: string;
}

Best Practices

  1. Use PascalCase for interface names
  2. Prefer interfaces for object shapes
  3. Use extends to create interface hierarchies
  4. Mark properties readonly when they shouldn't change
  5. Use optional properties sparingly
  6. Keep interfaces focused - single responsibility
  7. Document complex interfaces with comments
  8. Use declaration merging to extend library types
  9. Avoid prefix "I" (e.g., use User not IUser)
  10. Group related interfaces in the same file
best-practices-example.ts
// ✅ Good: Clear, focused interface
interface User {
  readonly id: number;
  name: string;
  email: string;
  age?: number;
}

// ✅ Good: Extending for hierarchy
interface Employee extends User {
  employeeId: number;
  department: string;
}

// ✅ Good: Composing multiple interfaces
interface DatabaseEntity {
  readonly id: number;
  createdAt: Date;
  updatedAt: Date;
}

interface Auditable {
  createdBy: number;
  updatedBy: number;
}

interface Product extends DatabaseEntity, Auditable {
  name: string;
  price: number;
}

// ❌ Avoid: Too many properties in one interface
interface GodInterface {
  // 50+ properties
  // Too broad, hard to maintain
}

// ❌ Avoid: Using "I" prefix
interface IUser {  // Don't do this
  name: string;
}

// ✅ Better: No prefix
interface User {
  name: string;
}

Key Takeaways

  • Interfaces define contracts for object shapes
  • Use ? for optional properties
  • Use readonly for immutable properties
  • Interfaces can extend other interfaces with extends
  • Classes can implement interfaces with implements
  • Index signatures allow dynamic property names
  • Declaration merging combines multiple interface declarations
  • Interfaces are best for object shapes and public APIs
  • Type aliases are better for unions, tuples, and primitives
  • Interfaces are extendable and can be implemented by classes

What's Next?

Fantastic work! You've mastered interfaces and understand when to use them vs type aliases. Next, we'll explore Optional and Default Parameters in more depth, learning advanced patterns for handling function parameters, parameter destructuring, and more. You'll become an expert at creating flexible, well-designed function signatures!

Get ready to level up your function parameter skills!

Mastering TypeScript interfaces! Check this out!

Previous
Function Types and Signatures
Next
Optional and Default Parameters

Never Miss a New TypeScript Tutorial

Join 1,000+ developers learning TypeScript step-by-step. Get new tutorials, type safety tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

TypeScript Tutorials

0 of 30 completed

Your Progress0%

Topics

Getting Started

  • What is TypeScript?
  • TypeScript vs JavaScript
  • Setting Up TypeScript
  • Your First TypeScript Program

Basic Types

  • Primitive Types
  • Arrays and Tuples
  • Objects and Type Aliases
  • Enums
  • Any, Unknown, and Never

Functions & Interfaces

  • Function Types and Signatures
  • Interfaces
  • Optional and Default Parameters
  • Function Overloading

Advanced Types

  • Union and Intersection Types
  • Type Guards and Narrowing
  • Literal Types
  • Type Assertions and Casting

Generics

  • Introduction to Generics
  • Generic Constraints
  • Utility Types

Classes & OOP

  • Classes and Constructors
  • Access Modifiers
  • Inheritance and Polymorphism
  • Abstract Classes and Interfaces

Practical TypeScript

  • Working with Modules
  • TypeScript with React
  • Async/Await and Promises
  • Working with APIs and JSON
  • Type Declarations and DefinitelyTyped
  • TypeScript Best Practices
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo