Understanding when to use interfaces versus types is essential for TypeScript development. Here's a comprehensive comparison.
Basic Syntax#
1// Interface
2interface User {
3 id: number;
4 name: string;
5 email: string;
6}
7
8// Type alias
9type UserType = {
10 id: number;
11 name: string;
12 email: string;
13};
14
15// Both work the same for object types
16const user1: User = { id: 1, name: 'Alice', email: 'alice@example.com' };
17const user2: UserType = { id: 2, name: 'Bob', email: 'bob@example.com' };Declaration Merging#
1// Interfaces can be merged
2interface Config {
3 apiUrl: string;
4}
5
6interface Config {
7 timeout: number;
8}
9
10// Results in:
11// interface Config {
12// apiUrl: string;
13// timeout: number;
14// }
15
16const config: Config = {
17 apiUrl: 'https://api.example.com',
18 timeout: 5000,
19};
20
21// Types cannot be merged
22type ConfigType = {
23 apiUrl: string;
24};
25
26// Error: Duplicate identifier 'ConfigType'
27// type ConfigType = {
28// timeout: number;
29// };
30
31// This is useful for extending third-party types
32declare module 'express' {
33 interface Request {
34 user?: User;
35 }
36}Extension and Intersection#
1// Interface extends
2interface Animal {
3 name: string;
4}
5
6interface Dog extends Animal {
7 breed: string;
8}
9
10// Multiple inheritance
11interface Pet extends Animal {
12 owner: string;
13}
14
15interface PetDog extends Dog, Pet {
16 trained: boolean;
17}
18
19// Type intersection
20type AnimalType = {
21 name: string;
22};
23
24type DogType = AnimalType & {
25 breed: string;
26};
27
28// Interface extends type
29interface Cat extends AnimalType {
30 meows: boolean;
31}
32
33// Type extends interface
34type Bird = Animal & {
35 canFly: boolean;
36};Unique Type Features#
1// Union types (only with type)
2type Status = 'pending' | 'active' | 'completed';
3type ID = string | number;
4
5// Cannot do this with interface
6// interface Status = 'pending' | 'active' // Error
7
8// Mapped types
9type Readonly<T> = {
10 readonly [P in keyof T]: T[P];
11};
12
13type Optional<T> = {
14 [P in keyof T]?: T[P];
15};
16
17// Conditional types
18type IsString<T> = T extends string ? true : false;
19
20type StringOrNumber<T> = T extends string
21 ? string
22 : T extends number
23 ? number
24 : never;
25
26// Template literal types
27type EventName = 'click' | 'focus' | 'blur';
28type EventHandler = `on${Capitalize<EventName>}`;
29// 'onClick' | 'onFocus' | 'onBlur'
30
31// Tuple types
32type Point = [number, number];
33type RGB = [number, number, number];
34
35// With labels (TypeScript 4.0+)
36type Point3D = [x: number, y: number, z: number];Unique Interface Features#
1// Implements (classes)
2interface Serializable {
3 serialize(): string;
4}
5
6interface Deserializable {
7 deserialize(data: string): void;
8}
9
10class Document implements Serializable, Deserializable {
11 content: string = '';
12
13 serialize(): string {
14 return JSON.stringify({ content: this.content });
15 }
16
17 deserialize(data: string): void {
18 const parsed = JSON.parse(data);
19 this.content = parsed.content;
20 }
21}
22
23// Call signatures
24interface Formatter {
25 (value: string): string;
26 locale: string;
27}
28
29const formatter: Formatter = Object.assign(
30 (value: string) => value.toUpperCase(),
31 { locale: 'en-US' }
32);
33
34// Construct signatures
35interface DateConstructor {
36 new (value: number): Date;
37 new (value: string): Date;
38}
39
40// Index signatures
41interface StringMap {
42 [key: string]: string;
43}
44
45interface NumberMap {
46 [key: number]: string;
47}Performance Considerations#
1// Interfaces are generally faster for:
2// - Error messages (cleaner type names)
3// - Type checking (cached by name)
4
5// Complex intersections can be slow
6type Complex = A & B & C & D & E & F & G;
7
8// Prefer interface extension
9interface Better extends A, B, C, D {}
10
11// For large codebases, interfaces can provide
12// better performance in the compilerPractical Guidelines#
1// Use INTERFACE for:
2
3// 1. Object shapes (most common case)
4interface User {
5 id: number;
6 name: string;
7}
8
9// 2. Class contracts
10interface Repository<T> {
11 find(id: string): Promise<T>;
12 save(item: T): Promise<void>;
13}
14
15class UserRepository implements Repository<User> {
16 async find(id: string): Promise<User> {
17 // implementation
18 }
19 async save(user: User): Promise<void> {
20 // implementation
21 }
22}
23
24// 3. Extensible APIs
25interface PluginOptions {
26 name: string;
27}
28
29// Third parties can extend
30declare module 'my-plugin' {
31 interface PluginOptions {
32 customOption: boolean;
33 }
34}
35
36// Use TYPE for:
37
38// 1. Union types
39type Result<T> = T | Error;
40type Theme = 'light' | 'dark' | 'system';
41
42// 2. Function types
43type Handler = (event: Event) => void;
44type AsyncHandler = (event: Event) => Promise<void>;
45
46// 3. Computed types
47type Keys = keyof User;
48type UserValues = User[keyof User];
49
50// 4. Mapped types
51type Partial<T> = { [P in keyof T]?: T[P] };
52type Readonly<T> = { readonly [P in keyof T]: T[P] };
53
54// 5. Tuples
55type Coordinates = [number, number];
56type Response = [data: User[], total: number];
57
58// 6. Complex type expressions
59type DeepPartial<T> = T extends object
60 ? { [P in keyof T]?: DeepPartial<T[P]> }
61 : T;Common Patterns#
1// React component props - either works
2interface ButtonProps {
3 label: string;
4 onClick: () => void;
5}
6
7type ButtonPropsType = {
8 label: string;
9 onClick: () => void;
10};
11
12// API response types - type for unions
13type ApiResponse<T> =
14 | { status: 'success'; data: T }
15 | { status: 'error'; message: string };
16
17// Redux actions - type for discriminated unions
18type Action =
19 | { type: 'INCREMENT'; payload: number }
20 | { type: 'DECREMENT'; payload: number }
21 | { type: 'RESET' };
22
23// State machines
24type State =
25 | { status: 'idle' }
26 | { status: 'loading' }
27 | { status: 'success'; data: User }
28 | { status: 'error'; error: Error };
29
30// Generic utilities - type for flexibility
31type Nullable<T> = T | null;
32type Maybe<T> = T | null | undefined;
33type ValueOf<T> = T[keyof T];Combining Both#
1// Interface for structure, type for unions
2interface User {
3 id: number;
4 name: string;
5}
6
7interface Admin extends User {
8 permissions: string[];
9}
10
11interface Guest {
12 sessionId: string;
13}
14
15type AnyUser = User | Admin | Guest;
16
17// Function overloads with interface
18interface Calculator {
19 add(a: number, b: number): number;
20 add(a: string, b: string): string;
21}
22
23const calc: Calculator = {
24 add(a: any, b: any) {
25 return a + b;
26 },
27};
28
29// Props pattern
30interface BaseProps {
31 className?: string;
32 style?: React.CSSProperties;
33}
34
35type ComponentProps = BaseProps & {
36 variant: 'primary' | 'secondary';
37 size: 'small' | 'medium' | 'large';
38};Migration Considerations#
1// If you need to migrate from one to another:
2
3// Interface to type
4interface OldInterface {
5 prop: string;
6}
7
8type NewType = OldInterface; // Just alias
9
10// Type to interface (if it's an object type)
11type OldType = {
12 prop: string;
13};
14
15interface NewInterface extends OldType {}
16
17// Note: Not all types can become interfaces
18type Union = 'a' | 'b'; // Cannot be interface
19type Tuple = [string, number]; // Cannot be interfaceBest Practices#
Default Choice:
✓ Use interface for object types
✓ Use type for unions, tuples, mapped types
✓ Be consistent within a codebase
✓ Document team conventions
Interface When:
✓ Defining object shapes
✓ Creating class contracts
✓ Building extensible APIs
✓ You need declaration merging
Type When:
✓ Creating union types
✓ Creating tuple types
✓ Creating mapped/conditional types
✓ You need type computations
Avoid:
✗ Mixing styles inconsistently
✗ Over-engineering type definitions
✗ Complex deeply nested types
✗ Circular type references
Conclusion#
Both interfaces and types are powerful TypeScript features. Use interfaces for object shapes and class contracts where declaration merging might be useful. Use types for unions, tuples, and complex type computations. In practice, the choice often comes down to team conventions and specific use cases.