[번역] Type과 Interface 더 이상 헷갈려하지 마세요
Opened this issue · 0 comments
타입스크립트 마스터 시리즈에 오신 것을 환영합니다. 이 시리즈는 애니메이션 형태로 타입스크립트의 코어 지식과 기술들을 설명합니다. 같이 배워보아요! 이전 글들은 아래를 참고하세요
- What Are K, T, and V in TypeScript Generics?
- Using TypeScript Mapped Types Like a Pro
- Using TypeScript Conditional Types Like a Pro
- Using TypeScript infer Like a Pro
- Using TypeScript Template Literal Types Like a Pro
- TypeScript Visualized: 15 Most Used Utility Types
만약 여러분이 레주메에 타입스크립트 경력을 썼다면, 아마 면접관은 type과 interface의 차이점이 무엇인지 물어볼 겁니다. 어떻게 대답해야 할지 아시겠나요? 모르겠다면, 이 글을 읽고 잘 이해하실 수도 있을 것 같네요.
타입 별칭(alias)은 타입에 새로운 이름을 부여하기 위해 사용될 수 있고, 원시 타입과 같은 비객체 타입이나 합집합에 네이밍할 때도 도움이 됩니다.
type MyNumber = number;
type StringOrNumber = string | number;
type Text = string | string[];
type Point = [number, number];
type Callback = (data: string) => void;
Typescript 1.6에서 타입 별칭은 제너릭 타입을 지원하기 시작했습니다. 우리가 흔히 작업할 때 자주 사용하는 Partial, Required, Pick, Record, 그리고 Exclude와 같은 유틸리티 타입도 타입 별칭으로 정의됩니다.
객체 타입을 정의할 때 interface
가 주로 사용됩니다. Vue 3의 App 객체로 interface를 사용하여 정의되었습니다.
위 코드에서 볼 수 있듯이, interface를 정의할 때 객체 타입에 프로퍼티와 메소드 모두를 선언할 수 있습니다. 둘의 비슷한 점을 먼저 알려드리고 type과 interface의 역할을 이해해보도록 하겠습니다.
유사점
타입 별칭과 인터페이스 모두 객체나 함수 타입을 정의할 때 사용된다.
타입 별칭(type alias)
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;
위 코드에서 객체 리터럴 타입과 합수 타입에 type 키워드로 별칭을 부여함으로써 다른 곳에서도 이 타입들을 사용할 수 있습니다.
인터페이스
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void;
}
타입 별칭과 인터페이스 모두 확장 가능하다.
타입 별칭은 &
에 의해 확장되지만 인터페이스는 extends
에 의해 확장됩니다.
그렇다면 인터페이스가 타입 별칭으로 정의된 타입을 extends로 확장할 수 있을까요? 답은 '그렇다'입니다. 추가적으로 타입 별칭 또한 & 연산자를 이용하여 인터페이스로 정의된 타입을 확장할 수도 있습니다.
그럼 이제 타입 별칭과 인터페이스의 유사점을 알게 되셨나요? 그럼 차이점에 대해 얘기해봅시다.
차이점
- 타입 별칭은 원시 타입, 유니온 타입, 튜플 타입에 대해 별칭을 정의할 수 있지만 인터페이스는 그럴 수 없습니다.
type MyNumber = number; // primitive type
type StringOrNumber = string | number; // union type
type Point = [number, number]; // tuple type
- 동일한 이름의 인터페이스는 자동적으로 합쳐지지만(Declaration Merging, 선언 병합) 타입 별칭은 그렇지 않습니다.
선언 병합 기능을 사용하면 서드파티 라이브러리를 개발할 때 사용자에게 더 좋은 보안점을 제공할 수 있습니다. 예를 들어 webext-bridge 라이브러리는 ProtocolMap 인터페이스를 정의하기 위해 인터페이스를 사용하는데 이 때문에 사용자들은 편하게 ProtocolMap 인터페이스를 확장할 수 있습니다. 그 후, 라이브러리 내부에 제공되는 onMessage 함수를 사용하여 커스텀 메시지를 모니터링할 때 서로 다른 메시지에 해당하는 메세지 바디 타입들을 추론할 수 있습니다.
extends ProtocolMap interface
import { ProtocolWithReturn } from 'webext-bridge'
declare module 'webext-bridge' {
export interface ProtocolMap {
foo: { title: string }
bar: ProtocolWithReturn<CustomDataType, CustomReturnType>
}
}
listen for custom messages
import { onMessage } from 'webext-bridge'
onMessage('foo', ({ data }) => {
// type of `data` will be `{ title: string }`
console.log(data.title)
}
이 부분에 대해 흥미가 있으시다면 webext-bridge의 onMessage에 대한 타입 정의를 살펴보세요. 문제가 있다면 저와 대화하실 수 있어요. 마지막으로 타입 별칭과 인터페이스에 대한 몇몇 사용 시나리오를 요약해보겠습니다.
언제 type
을 사용할까요?
- 원시 타입에 별칭을 정의할 때는
type
을 쓰세요 - 튜플 타입을 정의할 때
type
을 쓰세요 - 함수 타입을 정의할 때
type
을 쓰세요 - 유니온 타입을 정의할 때
type
을 쓰세요 - 매핑된 타입을 정의할 때
type
을 쓰세요
언제 interface
을 사용할까요?
- 선언 병합 기능의 장점을 활용하려면
interface
를 쓰세요 - 객체 타입을 정의하고 type을 사용할 필요가 없을 때
interface
를 쓰세요
이 글을 읽고 여러분들이 타입 별칭과 인터페이스의 차이를 이미 이해하셨으리라 믿습니다.