목차
인터페이스(interface): 객체의 모양 선언
인터페이스는 객체가 제공해야 할 속성과 메서드를 TypeScript가 검사하도록 표현한다. Java의 인터페이스처럼 런타임 객체를 생성하지 않는다. 컴파일된 JavaScript에는 인터페이스 선언이 남지 않으므로 API에서 받은 JSON의 실제 모양은 따로 검증해야 한다.
interface Book {
title: string;
pages: number;
author?: string;
}
const book: Book = { title: "TypeScript", pages: 240 };
console.log(book.author ?? "저자 미상");
author?는 속성이 없을 수 있다는 뜻이다. 읽을 때 undefined 가능성을 처리한다. pages는 필수이므로 빠뜨리면 컴파일 오류다. 원문의 예제에서 author와 pages만 정의했다면 둘만 검사되며 제목 같은 필수 정보가 필요하면 위처럼 계약에 포함한다.
읽기 전용 속성의 범위
interface Page {
readonly text: string;
readonly metadata: { reviewed: boolean };
}
const page: Page = { text: "원문", metadata: { reviewed: false } };
// page.text = "수정"; // 컴파일 오류
page.metadata.reviewed = true; // 내부 객체는 수정 가능
readonly는 속성 재할당을 타입 검사에서 막는다. 인터페이스에서만 쓸 수 있는 문법은 아니다. type으로 선언한 객체 속성에도 쓸 수 있다. 런타임의 깊은 불변성을 보장하지 않으므로 중첩 객체까지 보호하려면 별도 설계가 필요하다. 원문의 read 함수 예제는 닫는 중괄호가 없고 일부러 타입 오류를 내는 할당이 섞여 있었으므로 독립적으로 읽을 수 있는 예제로 정리했다.
메서드와 함수 속성
interface Formatter {
format(value: string): string;
parse: (text: string) => string;
optional?(): void;
}
const formatter: Formatter = {
format(value) { return value.trim(); },
parse: text => text.toLowerCase(),
};
formatter.optional?.();
format(...)은 메서드 문법이고 parse: (...) => ...는 함수를 담는 속성 문법이다. 호출 모습은 비슷하지만 메서드의 매개변수 호환성 검사와 함수 속성의 검사에는 차이가 있을 수 있으므로 콜백 API를 설계할 때 구분한다. optional은 없어도 되므로 ?.()로 안전하게 호출한다.
중첩과 확장
interface Setting { place: string; year: number }
interface Writing { title: string | null }
interface Reading { name: string }
interface Novel extends Writing, Reading {
title: string; // 기반 타입의 string | null보다 좁은 타입
pages: number;
setting: Setting;
}
const novel: Novel = {
title: "Novella", name: "Kim", pages: 195,
setting: { place: "Seoul", year: 2026 },
};
extends는 여러 계약을 결합한다. 같은 속성을 다시 선언할 때는 기반 속성과 호환되어야 하므로 string | null을 string으로 좁히는 위 예제는 가능하지만, 무관한 number로 바꾸면 오류가 난다. 중첩된 setting도 지정한 Setting 모양으로 검사한다. 인터페이스가 같은 이름으로 여러 번 선언되면 합쳐지는 선언 병합도 가능하지만, 실수로 충돌시키지 않도록 공개 타입의 이름을 관리한다.