본문으로 건너뛰기 TypeScript Interfaces | Complete Guide

TypeScript Interfaces | Complete Guide

TypeScript Interfaces | Complete Guide

이 글의 핵심

Interfaces in TypeScript: optional and readonly props, call signatures, index signatures, extends, declaration merging, implements, and when to prefer interface vs type alias.

Introduction

An interface describes the shape of objects in TypeScript.

1. Interface basics

Declaration

An interface lists the properties an object must have and their types:

// User interface — defines the object "contract"
interface User {
    id: string;
    name: string;
    age: number;
    email: string;
}
// ✅ Valid: all required properties provided
const user: User = {
    id: "U001",
    name: "Alice",
    age: 25,
    email: "alice@test.com"
};
// ❌ Missing properties
// const user2: User = { id: "U002", name: "Bob" };
// ❌ Wrong types
// const user3: User = { ..., age: "25", ... };
// ❌ Unknown extra properties (in object literals with explicit type)
// const user4: User = { ..., phone: "..." };

What interfaces give you:

  1. Type checking: validate object structure at compile time
  2. Autocomplete: IDE suggests property names
  3. Documentation: the shape is explicit
  4. Refactoring: rename or change types with confidence

Optional properties

Use ? for optional properties:

interface User {
    id: string;
    name: string;
    age?: number;
    email?: string;
}
// ✅ OK without optional fields
const user1: User = {
    id: "U001",
    name: "Alice"
};
// ✅ OK with optional fields
const user2: User = {
    id: "U002",
    name: "Bob",
    age: 30,
    email: "bob@test.com"
};
function printAge(user: User) {
    if (user.age !== undefined) {
        console.log(user.age.toFixed(0));
    }
    console.log(user.age?.toFixed(0));
    const age = user.age ?? 0;
    console.log(age);
}
printAge(user1);
printAge(user2);

Why optional properties help:

  • Flexible shapes
  • Partial updates
  • APIs where some fields may be absent

Readonly properties

readonly marks properties that must not be reassigned after initialization:

// 타입 정의
interface User {
    readonly id: string;
    name: string;
    age: number;
}
const user: User = {
    id: "U001",
    name: "Alice",
    age: 25
};
user.name = "Bob";  // ✅
user.age = 30;      // ✅
// user.id = "U002";   // ❌ error
interface Post {
    readonly id: string;
    readonly createdAt: Date;
    title: string;
    content: string;
    updatedAt: Date;
}
const post: Post = {
    id: "POST001",
    createdAt: new Date(),
    title: "First post",
    content: "Body",
    updatedAt: new Date()
};
post.title = "Updated title";
post.content = "Updated body";
post.updatedAt = new Date();

readonly vs const:

  • readonly: property on an object
  • const: binding for a variable
const user: User = { id: "U001", name: "Alice", age: 25 };
// user cannot be reassigned
// user.id cannot be reassigned

2. Function types in interfaces

Methods

interface Calculator {
    add(a: number, b: number): number;
    subtract(a: number, b: number): number;
    multiply?(a: number, b: number): number;
}
const calc: Calculator = {
    add(a, b) {
        return a + b;
    },
    subtract(a, b) {
        return a - b;
    }
};
console.log(calc.add(10, 5));
console.log(calc.subtract(10, 5));

Call signatures

interface MathOperation {
    (a: number, b: number): number;
}
const add: MathOperation = (a, b) => a + b;
const multiply: MathOperation = (a, b) => a * b;
console.log(add(10, 5));
console.log(multiply(10, 5));

Constructor types

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick(): void;
}
class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) {}
    tick() {
        console.log("beep beep");
    }
}
function createClock(
    ctor: ClockConstructor,
    hour: number,
    minute: number
): ClockInterface {
    return new ctor(hour, minute);
}
const clock = createClock(DigitalClock, 12, 17);
clock.tick();

3. Index signatures

String index

interface StringMap {
    [key: string]: string;
}
const colors: StringMap = {
    red: "#FF0000",
    green: "#00FF00",
    blue: "#0000FF"
};
console.log(colors[red]);
console.log(colors.green);

Numeric index

interface NumberArray {
    [index: number]: string;
}
const fruits: NumberArray = ["apple", "banana", "orange"];
console.log(fruits[0]);
console.log(fruits[1]);

Mixed constraints

interface Dictionary {
    [key: string]: string | number;
    length: number;
}
const dict: Dictionary = {
    name: "Alice",
    age: 25,
    length: 2
};

4. Extending interfaces

extends

interface Person {
    name: string;
    age: number;
}
interface Employee extends Person {
    employeeId: string;
    department: string;
}
const employee: Employee = {
    name: "Alice",
    age: 30,
    employeeId: "E001",
    department: "Engineering"
};

Multiple inheritance

interface Timestamped {
    createdAt: Date;
    updatedAt: Date;
}
interface Identifiable {
    id: string;
}
interface User extends Identifiable, Timestamped {
    name: string;
    email: string;
}
const user: User = {
    id: "U001",
    name: "Alice",
    email: "alice@test.com",
    createdAt: new Date(),
    updatedAt: new Date()
};

5. Interface merging

Declaration merging

interface User {
    name: string;
}
interface User {
    age: number;
}
const user: User = {
    name: "Alice",
    age: 25
};

Augmenting globals

interface Window {
    myCustomProperty: string;
}
window.myCustomProperty = "Hello!";
console.log(window.myCustomProperty);

6. Classes and interfaces

implements

interface Animal {
    name: string;
    makeSound(): void;
}
class Dog implements Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    makeSound() {
        console.log("Woof!");
    }
}
const dog = new Dog("Buddy");
dog.makeSound();

Multiple interfaces

interface Flyable {
    fly(): void;
}
interface Swimmable {
    swim(): void;
}
class Duck implements Flyable, Swimmable {
    fly() {
        console.log("Flying!");
    }
    swim() {
        console.log("Swimming!");
    }
}
const duck = new Duck();
duck.fly();
duck.swim();

7. Interface vs type alias

Comparison

FeatureInterfaceType alias
Object shapes
Union / intersection❌ (use type)
Extensionextends&
Declaration merging
Primitive aliases

Examples

interface User {
    name: string;
}
interface User {
    age: number;
}
type Person = {
    name: string;
};
type ID = string | number;
type Status = "active" | "inactive";
type Employee = Person & {
    employeeId: string;
};

8. Hands-on examples

Example 1: API response

interface ApiResponse<T> {
    success: boolean;
    data: T;
    error?: string;
    timestamp: Date;
}
interface User {
    id: string;
    name: string;
    email: string;
}
async function fetchUser(id: string): Promise<ApiResponse<User>> {
    try {
        const response = await fetch(`/api/users/${id}`);
        const data = await response.json();
        return {
            success: true,
            data,
            timestamp: new Date()
        };
    } catch (error) {
        return {
            success: false,
            data: null as any,
            error: "Request failed",
            timestamp: new Date()
        };
    }
}

Example 2: Form validation

interface FormField {
    value: string;
    error: string | null;
    touched: boolean;
}
interface LoginForm {
    email: FormField;
    password: FormField;
}
const form: LoginForm = {
    email: {
        value: "",
        error: null,
        touched: false
    },
    password: {
        value: "",
        error: null,
        touched: false
    }
};
function validateEmail(email: string): string | null {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email) ? null : "Invalid email format";
}
form.email.value = "alice@test.com";
form.email.error = validateEmail(form.email.value);
form.email.touched = true;

Example 3: Event handlers

interface ClickEvent {
    x: number;
    y: number;
    button: "left" | "right";
}
interface EventHandler<T> {
    (event: T): void;
}
const handleClick: EventHandler<ClickEvent> = (event) => {
    console.log(`Click: (${event.x}, ${event.y}), button: ${event.button}`);
};
handleClick({ x: 100, y: 200, button: "left" });

Summary

Takeaways

  1. Interface: describe object structure
  2. Optional: ?
  3. Readonly: readonly
  4. Extension: extends (multiple allowed)
  5. Implementation: implements (multiple allowed)
  6. Merging: declaration merging

When to use interfaces

  • Object models
  • Class contracts
  • Augmenting libraries
  • API response types

Next steps



Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Interfaces in TypeScript: optional and readonly props, call signatures, index signatures, extends, declaration merging, …

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers TypeScript, Interface, extends, implements, Declaration Merging.