Interfaces are how TypeScript describes the shape of your data. In test automation they keep test data, API responses and page objects consistent, so typos are caught in the editor instead of in a failed CI run. This guide explains interfaces from scratch with simple examples.
📖 What You’ll Learn: interface syntax, optional and readonly properties, default values, methods in interfaces, extends, index signatures, interface vs type, interface vs class, and how to use interfaces in Playwright tests.
📐 What Is an Interface in TypeScript?
An interface is a named description of what an object must look like: which properties it has, what type each one is, and which methods it provides. Think of it as a contract or a blueprint. Any object that claims to follow the interface must match it, or TypeScript shows an error before your code runs.
Interfaces do not exist at runtime. They are removed when TypeScript compiles to JavaScript, so they add safety without adding any code to your tests.
interface User {
username: string;
password: string;
age: number;
}
const tester: User = {
username: 'standard_user',
password: 'secret_sauce',
age: 28,
};
// ❌ Error: Property 'age' is missing in type
// const broken: User = { username: 'x', password: 'y' };
💡 Tip: By convention, interface names use PascalCase (User, LoginPage, ApiResponse). Do not add an I prefix like IUser; the TypeScript team recommends against it.
🧩 Interface Example: Optional and Readonly Properties
Just like object types, interface properties can be optional (?) or readonly.
interface TestEnvironment {
readonly name: string; // cannot change after creation
baseUrl: string;
apiKey?: string; // optional
timeout?: number; // optional
}
const qa: TestEnvironment = { name: 'QA', baseUrl: 'https://qa.sqatools.in' };
qa.baseUrl = 'https://qa2.sqatools.in'; // ✅
// qa.name = 'PROD'; // ❌ read-only
🎚️ Default Values for Interface Properties
A common beginner question is “how do I give an interface a default value?”. Interfaces only describe types, so they cannot hold values. Instead, set defaults where the object is created, usually with the spread operator or default parameters.
interface BrowserOptions {
browser: 'chromium' | 'firefox' | 'webkit';
headless?: boolean;
slowMo?: number;
}
const defaultOptions: Required<BrowserOptions> = {
browser: 'chromium',
headless: true,
slowMo: 0,
};
function createOptions(custom: Partial<BrowserOptions> = {}): Required<BrowserOptions> {
return { ...defaultOptions, ...custom };
}
createOptions({ headless: false });
// { browser: 'chromium', headless: false, slowMo: 0 }
📝 Note: Partial<T> makes every property optional and Required<T> makes every property required. They are built-in utility types that work with any interface.
🛠️ Methods and Functions in Interfaces
Interfaces can describe methods too. This is exactly how you describe what a page object must be able to do.
interface LoginActions {
open(): Promise<void>;
login(username: string, password: string): Promise<void>;
getErrorMessage(): Promise<string>;
}
// An interface can also describe a standalone function type
interface Formatter {
(value: number): string;
}
const toPrice: Formatter = (value) => '₹' + value.toFixed(2);
console.log(toPrice(499)); // ₹499.00
🔗 Extending Interfaces
Interfaces can extend other interfaces to reuse properties. The new interface gets everything from the parent plus its own properties. You can extend more than one interface at once.
interface Person {
firstName: string;
lastName: string;
}
interface Employee extends Person {
employeeId: number;
department: string;
}
interface Timestamps {
createdAt: Date;
}
interface Admin extends Employee, Timestamps {
permissions: string[];
}
const lead: Admin = {
firstName: 'Ravi',
lastName: 'Kumar',
employeeId: 101,
department: 'QA',
createdAt: new Date(),
permissions: ['create-user', 'delete-user'],
};
📚 Interfaces for Arrays, Nested Data and Dynamic Keys
interface Address { city: string; zip: string }
interface Customer {
id: number;
name: string;
address: Address; // nested interface
tags: string[]; // array of strings
orders: { id: number; total: number }[]; // array of objects
}
// Index signature: any number of string keys
interface Headers {
[header: string]: string;
}
const headers: Headers = { 'Content-Type': 'application/json', Accept: '*/*' };
⚔️ Interface vs Type: What Is the Difference?
This is one of the most searched TypeScript questions. For describing objects, both work and look almost the same. The differences:
| Feature | interface | type |
|---|---|---|
| Describe object shapes | ✅ Yes | ✅ Yes |
| Extend / reuse | extends keyword | Intersection & |
Union types ('a' | 'b') | ❌ No | ✅ Yes |
| Primitives and tuples | ❌ No | ✅ Yes |
| Declaration merging (same name twice) | ✅ Merges | ❌ Error |
| Implemented by classes | ✅ Yes | ✅ Yes (object types) |
// Same shape, two ways
interface UserA { name: string }
type UserB = { name: string };
// Extending
interface AdminA extends UserA { role: string }
type AdminB = UserB & { role: string };
// Only a type can do this
type Browser = 'chromium' | 'firefox' | 'webkit';
type Point = [number, number];
// Only an interface can do this (declaration merging)
interface Settings { theme: string }
interface Settings { language: string }
const s: Settings = { theme: 'dark', language: 'en' };
💡 Tip: A simple rule for beginners: use interface for objects and class contracts (page objects, API models), and use type for unions, tuples and combinations. Be consistent within your project.
🏛️ Interface vs Class
An interface only describes shape and is removed at compile time. A class creates real objects at runtime and can contain code, constructors and default values. A class can implement an interface to promise it follows that contract.
interface Logger {
log(message: string): void;
}
class ConsoleLogger implements Logger {
log(message: string): void {
console.log('[test] ' + message);
}
}
// ❌ Error if the class forgets the log() method
🎭 Interfaces in Playwright: Real Examples
1. Typing test data
// test-data/users.ts
export interface UserData {
username: string;
password: string;
expectedPage: string;
}
export const users: Record<'standard' | 'problem', UserData> = {
standard: { username: 'standard_user', password: 'secret_sauce', expectedPage: '/inventory.html' },
problem: { username: 'problem_user', password: 'secret_sauce', expectedPage: '/inventory.html' },
};
2. Typing an API response
import { test, expect } from '@playwright/test';
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
test('GET a post returns the correct shape', async ({ request }) => {
const response = await request.get('https://jsonplaceholder.typicode.com/posts/1');
expect(response.ok()).toBeTruthy();
const post: Post = await response.json();
expect(post.id).toBe(1);
expect(typeof post.title).toBe('string'); // editor autocompletes post.title
});
3. A contract for page objects
import { Page } from '@playwright/test';
interface BasePage {
readonly page: Page;
readonly url: string;
open(): Promise<void>;
}
export class LoginPage implements BasePage {
readonly url = 'https://www.saucedemo.com/';
constructor(readonly page: Page) {}
async open() {
await this.page.goto(this.url);
}
async login(user: { username: string; password: string }) {
await this.page.getByPlaceholder('Username').fill(user.username);
await this.page.getByPlaceholder('Password').fill(user.password);
await this.page.getByRole('button', { name: 'Login' }).click();
}
}
Read the full pattern in Playwright Page Object Model step by step.
✅ Best Practices
- Name interfaces after what they represent:
UserData,ApiResponse,CheckoutPage. - Keep shared interfaces in one folder (for example
types/) and export them. - Use
extendsto build specific interfaces from general ones instead of copy-pasting properties. - Type every API response you assert on. Autocomplete then prevents typos in property names.
- Prefer
Partial<T>for “update” or “override” objects instead of making every property optional.
❓ FAQ
🤔 What is an interface in TypeScript in simple words?
It is a named blueprint that lists the properties and methods an object must have. TypeScript checks every object against it while you write code.
🤔 Should I use interface or type in TypeScript?
Both work for objects. Use interface for object shapes and class contracts, and type for unions, tuples and other combinations. Pick one style per project and stay consistent.
🤔 Can a TypeScript interface have default values?
No. Interfaces only describe types. Set defaults when creating the object, for example const opts = { ...defaults, ...custom }.
🤔 Can an interface extend multiple interfaces?
Yes: interface Admin extends Employee, Timestamps {} combines all properties from both parents.
🤔 Do interfaces slow down my tests?
No. Interfaces are removed during compilation and produce zero JavaScript, so they have no runtime cost.
🎯 Next Steps
- 📘 TypeScript Objects
- 📘 TypeScript Classes: implement interfaces in classes
- 📘 TypeScript Generics: reusable interfaces like
ApiResponse<T> - 📘 TypeScript with Playwright
- 🎭 Playwright with TypeScript Training Notes
🎬 Watch the full TypeScript tutorial series on YouTube!
Subscribe to SqaToolsByDY • Practice on sqatools.in
3 thoughts on “TypeScript Interfaces Explained: Examples, Interface vs Type, and Playwright Use Cases”