Interface vs Type
# Interface vs Type
둘 다 TypeScript에서 값의 모양(구조)을 적는 방법이다. 객체 프로퍼티, 함수 시그니처 등을 설명할 때 쓴다. 문법만 다르고 겹치는 부분이 많다.
차이는 어디까지 표현할 수 있나와 같은 이름으로 여러 번 선언할 수 있나에 있다!
# 한 줄 요약
interface |
type |
|
|---|---|---|
| 주 용도 | 객체 형태를 이름 붙여 정의 | 모든 종류의 타입 별칭 (객체, 유니온, 튜플 등) |
| 같은 이름 재선언 | 가능 (선언이 합쳐짐) | 불가능 (이름 충돌) |
extends |
다른 interface / class 확장 |
교차 타입 & 등으로 조합 |
# 공통으로 할 수 있는 것
둘 다 객체 모양을 이렇게 쓸 수 있다.
interface User {
id: number;
name: string;
}
type User2 = {
id: number;
name: string;
};
둘 다 implements에 쓸 수 있고 제네릭도 비슷하게 붙인다.
# interface만의 특징
# 1. 선언 병합(Declaration Merging)
같은 이름의 interface를 여러 파일/여러 번 선언하면 하나로 합쳐진다.
interface Window {
myApp: { version: string };
}
// 다른 파일에서 또 선언해도 Window에 myApp이 합쳐짐
라이브러리 타입을 조금씩 덧붙일 때 유용하다. 반면 type은 같은 이름을 두 번 쓰면 에러다.
# 2. 객체 중심의 확장
extends로 다른 인터페이스를 이어붙이기 읽기 쉽다.
interface Animal {
name: string;
}
interface Dog extends Animal {
bark(): void;
}
# type만 잘 하는 것 (또는 여기서 자연스러운 것)
# 1. 유니온·프리미티브·튜플
한 이름이 여러 모양 중 하나일 때는 type이 자연스럽다.
type Id = string | number;
type Point = [number, number];
type Status = 'idle' | 'loading' | 'error';
interface는 객체 한 덩어리에 맞춰져 있어서 위처럼 문자열 리터럴 유니온만 담는 식은 type이 더 흔하다.
# 2. 조건부 타입·매핑 등 고급 문법
type 별칭 쪽에서 조건부 타입, infer, 매핑된 타입 등을 많이 쓴다. (인터페이스로는 표현이 어렵거나 안 쓰는 패턴이다.)
# 3. 교차 타입 &
객체를 합칠 때 type A = B & C 패턴이 많다. interface는 extends로 비슷하게 할 수 있지만, 유니온에 &를 붙이는 식은 type이 필요하다.
type A = { a: number };
type B = { b: string };
type AB = A & B;
# 타입 범위 차이
# interface
- 주로 객체 형태(객체 리터럴, 클래스 인터페이스)에 이름을 붙이는 데 사용
interface User {
id: number;
name: string;
}
number,string, 유니온, 튜플 같은 원시/조합 타입 이름을 만들기에는 불편
# type
- 모든 타입에 이름을 줄 수 있다.
type Status = "pending" | "success" | "failed";
type Coordinates = [number, number];
type ApiResponse<T> = { data: T; status: number };
- 객체 타입도 가능하지만, 객체 스키마를 정의하는 용도라면
interface를 더 선호
# 확장 방식 차이
# interface
- 같은 이름으로 여러 번 정의해서 자동 병합(declaration merging).
interface User {
id: number;
}
interface User {
name: string;
}
// User = { id: number; name: string }
- 라이브러리 확장,
Window같은 전역 타입에 속성 추가 시 유용
# type
- 같은 이름으로 다시 선언하면 에러
type User = { id: number };
type User = { name: string }; // ❌ Duplicate identifier
- 대신
&로 합성해서 확장
type User = { id: number } & { name: string };
# 계산된 속성 / 고급 타입 옵션
# type
mapped types,keyof,in,extends같은 것을 조합해서 동적으로 속성 이름을 만드는 타입을 만들 수 있다.
type Keys = "a" | "b";
type Obj = { [K in Keys]: string };
- 이런 패턴은
interface에서는불가능
# interface
- 단순한 객체 모양을 정의하는 데 최적화됨
- 속성 충돌을 잘 감지
- 성능·읽기 측면에서 더 깔끔하게 합성
# 사용 예시
# interface
- API 응답, DTO, 컴포넌트 props, 서비스 인터페이스 등
→ 객체 스키마/모양을 정의할 때
interface UserDto {
id: number;
name: string;
}
# type
- 유니온 타입, 튜플,
const Status = "a" | "b" | "c", 복잡한 타입 헬퍼 등
→ 타입 계산/조합/원시형 이름 붙일 때
type Status = "success" | "error";
type ApiResponse<T> = {
data: T;
status: Status;
};
# 정리
- 객체의 모양(스키마) 이름을 짓는 것이면
interface - 모든 종류 타입(특히 유니온/튜플/고급 타입) 이름을 짓는 것이면
type
작년에 학교에서 기본적인 Typescript 문법에 대해 공부했던 적이 있었다. 근데 그 이후에 뭔가 깊게 파 본 적은 없기도 하고, 요즘같이 바이브 코딩 시대에서 뭔가 생각없이 일 하고 있다는 생각이 계속 들었다… 물론 클로드가 있어서 그걸 이용하면 되지만 어느정도 유지보수하고 코드를 이해하는 주최자는 나여야 된다고 생각한다. 클로드가 다 해 줘도 어느정도는 이 코드에 대해 이해해야지 내가 추후에 설계를 다시 하거나 리팩토링을 할 수 있을 것이다. 그래서 지금부터 TypeScript 문법에 대해 공부해 보고자 한다!
오늘은 interface와 type의 차이와 정의에 대해 한번 다시 봐봤다. 작년에 배웠던 기억이 떠올랏기도 해서 왠지 더 반가운(?)… 이렇게라고 다시 한번 보면서 어느정도 생각하고 이해해 보고 싶다. 물론 속도도 중요하지만 아직 계속해서 배워나가는 단계이니 계속해서 머릿속에 물음표를 달고 살아야겠다