1. 개요
interface User {
name: string
}
type User2 = {
name: string
}이 둘은 거의 같은 걸 선언한다. 그래서 “아무거나 써도 된다” 는 말을 자주 듣는데, 완전히 같지는 않다. 프로젝트 안에서 기준 없이 섞어 쓰면 나중에 “왜 이건 interface 고 저건 type 이지” 하는 리뷰 코멘트가 반복된다. 이번 편은 실제로 다른 지점 위주로 정리한다.
2. 핵심 내용
2-1. 겹치는 부분: 객체 모양 선언
객체 타입을 선언하는 용도로는 둘이 거의 동일하다. 확장도 둘 다 된다.
interface Animal {
name: string
}
interface Dog extends Animal {
breed: string
}
type Animal2 = {
name: string
}
type Dog2 = Animal2 & {
breed: string
}interface 는 extends 로, type 은 & 로 확장한다는 문법 차이만 있을 뿐 결과는 같다. 다만 이 extends는 컴파일하면 그냥 사라지는 타입 레벨 표시일 뿐이다. 자바스크립트의 class extends는 실제로 런타임에 프로토타입 체인을 연결하는 것과 다르다 — 그 동작은 자바스크립트 기초 4 - 프로토타입 체인과 class에서 다뤘다.
2-2. type 만 되는 것: 유니온, 원시 타입 별칭, 튜플
interface 는 객체(와 함수, 클래스) 모양만 선언할 수 있다. 유니온이나 원시 타입에는 못 쓴다.
type Status = "pending" | "approved" | "rejected" // interface 로는 불가능
type Id = string | number
type Coord = [number, number] // 튜플
interface Status {} // 이런 식으로 유니온을 표현할 방법이 없다2편에서 본 태그된 유니온, 이런 원시 타입 조합, 함수 타입 별칭까지 전부 type 의 영역이다. interface 로는 아예 표현이 안 되니 이건 취향 문제가 아니라 기능 차이다.
2-3. interface 만 되는 것: 선언 병합
같은 이름으로 interface 를 두 번 선언하면 자동으로 합쳐진다.
interface Window {
myGlobalFlag: boolean
}
interface Window {
anotherFlag: string
}
// 최종적으로 Window 는 두 필드를 모두 가진 것으로 합쳐진다
declare const w: Window
w.myGlobalFlag
w.anotherFlagtype 으로 같은 걸 시도하면 에러가 난다.
type Config = { a: string }
type Config = { b: string } // Error: 중복 식별자 'Config'선언 병합은 보통 직접 쓸 일은 적지만, 라이브러리 타입 정의를 프로젝트에서 확장할 때(예: Express 의 Request 에 커스텀 필드를 추가하는 declare global 패턴, 또는 .d.ts 에서 전역 타입을 보강할 때) 필수적으로 쓰인다. 이게 필요하다면 반드시 interface 여야 한다.
2-4. 에러 메시지의 가독성
사소하지만 실무에 영향을 주는 차이. interface 로 선언한 타입은 에러 메시지에 이름이 그대로 나온다. 복잡하게 중첩된 type 은 펼쳐진 형태로 나올 때가 있어 읽기 불편할 수 있다. 이 차이는 TypeScript 버전과 상황에 따라 편차가 있어서 절대적인 기준은 아니지만, 복잡한 객체 타입일수록 interface 쪽이 에러를 좀 더 깔끔하게 보여주는 경향이 있다.
2-5. 실질적 기준
기능 차이를 정리하면 선택 기준은 이렇게 된다.
- 유니온, 튜플, 원시 타입 별칭, 함수 타입, 매핑된 타입이 필요하면 무조건
type. 선택지가 없다. - 공개 API 로 노출되는 객체 모양(라이브러리의 옵션 타입, 다른 팀이 확장할 수도 있는 타입)이면
interface. 선언 병합으로 나중에 필드를 보강할 여지를 남긴다. - 둘 다 되는 평범한 객체 모양(컴포넌트 props, 일반 DTO)이라면 팀 컨벤션을 따르면 된다. TypeScript 공식 핸드북도 “가능하면
interface를 쓰고, 유니온이 필요하면type을 쓰라” 는 정도의 느슨한 권고만 한다.
중요한 건 “뭐가 더 우월한가” 가 아니라 유니온이 필요한 순간 interface 로는 막힌다는 것을 미리 알고 설계하는 것이다. 처음에 interface 로 시작했다가 나중에 “이 타입, 다른 타입이랑 유니온으로 묶어야 하네” 싶어서 type 으로 바꾸는 리팩터링은 흔하다.
3. 마무리
요약
- 객체 모양 선언은
interface와type이 거의 동일하다. 확장 문법만extends대&로 다르다.- 유니온·튜플·원시 타입 별칭·함수 타입은
type만 가능하다.interface로는 표현할 수 없다.- 같은 이름으로 여러 번 선언해 자동으로 합쳐지는 선언 병합은
interface만 가능하다. 전역 타입 보강에 쓴다.- 공개 API 모양은
interface, 유니온이 얽히면type. 나머지는 팀 컨벤션을 따르면 된다.
다음은 6편이다. unknown 과 any 의 차이를 다룬다.