"interface는 객체가 어떤 약속을 지켜야 하는지 말하고, 필요하면 그 약속을 다른 선언이 이어서 확장하게 해."
객체와 클래스의 모양
interface User { id: number; name: string }은 User로 쓰일 값이 두 속성을 가져야 한다고 선언해. 값이 어디서 만들어졌는지는 중요하지 않아. 필요한 모양을 만족하면 객체 리터럴도, 클래스 인스턴스도, 다른 인터페이스를 구현한 값도 들어올 수 있어.
메서드는 greet(): string처럼 적고, 선택 속성에는 ?, 읽기 전용에는 readonly를 붙여. 함수 호출 모양도 interface F { (x: number): string }처럼 나타낼 수 있어.
extends로 계약을 이어
interface Admin extends User처럼 기존 인터페이스의 속성을 물려받고 새 속성을 더할 수 있어. 여러 인터페이스를 함께 확장해 계약을 조합할 수도 있어.
클래스는 implements로 interface 계약을 지킨다고 선언할 수 있어. 컴파일러는 필요한 공개 멤버가 있는지 확인하지만 실행 코드에 별도 검사기를 넣지는 않아. 계약 확인은 어디까지나 컴파일 시점이야.
선언 병합이라는 고유한 힘
같은 이름의 interface를 여러 번 선언하면 TypeScript가 하나로 합쳐. 브라우저의 Window나 외부 라이브러리의 설정 타입에 속성을 보태는 모듈 확장에서 유용해. type 별칭은 같은 이름을 다시 선언할 수 없으므로 이 지점이 둘의 실제 차이 가운데 하나야.
선언 병합은 강력하지만 아무 파일에서나 몰래 확장하면 계약이 어디서 완성되는지 찾기 어려워져. 라이브러리 확장처럼 의도가 분명한 경계에 쓰고, 앱 내부의 평범한 모델은 한곳에서 선언하는 편이 좋아.
선언 병합은 lib.dom.d.ts의 브라우저 타입이나 Express.Request 같은 라이브러리 인터페이스를 보강할 때 특히 유용해. 같은 이름을 다시 선언하면 기존 계약에 새 속성이 합쳐져.