Q1 of 38 · TypeScript

What is the difference between interface and type alias?

TypeScriptMidtypescript-fundamentalsinterfacestype-aliasestype-system

Short answer

Short answer: Both define object shapes, but they differ in key ways: interfaces support declaration merging and are preferred for public contracts classes implement; type aliases handle unions, intersections, and mapped/conditional types. For plain object shapes in test code, either works — consistency matters more than the choice.

Detail

The surface syntax looks nearly identical:

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

Declaration merging is the clearest functional difference. You can declare the same interface multiple times and TypeScript merges the declarations — useful for augmenting library types (e.g., extending the global Window interface for test utilities). type aliases cannot be merged; a duplicate declaration is a compile error.

Expressiveness: type aliases can represent things that interface cannot:

  • Union types: type Status = "pass" | "fail" | "skip"
  • Intersection types: type AdminUser = User & AdminRole
  • Mapped types: type Optional<T> = { [K in keyof T]?: T[K] }
  • Conditional types: type NonNullable<T> = T extends null | undefined ? never : T

In test code, the practical guidance: use interface when defining the shape of objects that consumers will implement or extend (Page Objects, fixture contracts, service adapters). Use type for unions, utility compositions, and internal helpers. Codebase consistency matters more than the preference — pick one for plain object shapes and stick to it. Most modern TypeScript style guides (including the TypeScript team's own) say interface for extendable shapes, type for everything else.

// EXAMPLE

interface-vs-type.ts

// interface — Page Object contract (classes implement this)
interface LoginPage {
  navigate(): Promise<void>;
  fillCredentials(email: string, password: string): Promise<void>;
  submit(): Promise<void>;
  getErrorMessage(): Promise<string>;
}

// type — for unions and utility compositions
type TestLevel = "smoke" | "regression" | "e2e" | "contract";
type TestResult = { passed: boolean; duration: number; error?: string };

// Declaration merging (interface only — augment a library type)
interface Window {
  __test_flags__: Record<string, boolean>;
}

// Mapped type — only possible with type alias
type Partial<T> = { [K in keyof T]?: T[K] };

// Intersection — also only type
type AuthenticatedPage = LoginPage & { logout(): Promise<void> };

// MODEL ANSWER

Both define the shape of an object, and for basic object types they are interchangeable. The functional differences show up in three places. First, declaration merging: you can declare the same interface twice and TypeScript merges the declarations, which is useful for augmenting library types — extending the global Window interface with test utility flags, for example. A type alias gives you a compile error if you declare it twice. Second, expressiveness: type aliases handle things interfaces cannot — unions, intersection types, mapped types, and conditional types. A union like type Status equals pass or fail or skip can only be written as a type. Third, implementability: when you want a contract that a class will implement, interface gives cleaner error messages and makes the intent explicit. My practical guideline for test code is to use interface for Page Object contracts and service adapters — the shapes that classes implement — and to use type for status unions, result shapes, fixture factory return types, and any utility composition. Codebase consistency matters more than the specific choice, but that split gives you a principled reason for each decision.

// WHAT INTERVIEWERS LOOK FOR

Declaration merging as the concrete functional difference, that type aliases handle unions/mapped/conditional types, and a practical guideline for test code. Strong answers cite at least one testing-specific scenario where the distinction matters.

// COMMON PITFALL

Saying one is always better. The real answer is contextual — interface for extensible contracts that classes or objects implement, type for expressive compositions and unions.