holdanddeepdive / typescript-study

4 stars 0 forks source link

No More Confusion About TypeScript’s Type and Interface #10

Open sbyeol3 opened 1 year ago

sbyeol3 commented 1 year ago

원문 : https://javascript.plainenglish.io/no-more-confusion-about-typescripts-type-and-interface-63c39418ae35

image

타입스크립트 마스터 시리즈에 오신 것을 환영합니다. 이 시리즈는 애니메이션 형태로 타입스크립트의 코어 지식과 기술들을 설명합니다. 같이 배워보아요! 이전 글들은 아래를 참고하세요

만약 여러분이 레주메에 타입스크립트 경력을 썼다면, 아마 면접관은 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와 같은 유틸리티 타입도 타입 별칭으로 정의됩니다.

image

객체 타입을 정의할 때 interface가 주로 사용됩니다. Vue 3의 App 객체로 interface를 사용하여 정의되었습니다.

image

위 코드에서 볼 수 있듯이, 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에 의해 확장됩니다.

image

image

그렇다면 인터페이스가 타입 별칭으로 정의된 타입을 extends로 확장할 수 있을까요? 답은 '그렇다'입니다. 추가적으로 타입 별칭 또한 & 연산자를 이용하여 인터페이스로 정의된 타입을 확장할 수도 있습니다.

image

image

그럼 이제 타입 별칭과 인터페이스의 유사점을 알게 되셨나요? 그럼 차이점에 대해 얘기해봅시다.

차이점

  1. 타입 별칭은 원시 타입, 유니온 타입, 튜플 타입에 대해 별칭을 정의할 수 있지만 인터페이스는 그럴 수 없습니다.
type MyNumber = number; // primitive type
type StringOrNumber = string | number; // union type
type Point = [number, number]; // tuple type
  1. 동일한 이름의 인터페이스는 자동적으로 합쳐지지만(Declaration Merging, 선언 병합) 타입 별칭은 그렇지 않습니다.

image

image

선언 병합 기능을 사용하면 서드파티 라이브러리를 개발할 때 사용자에게 더 좋은 보안점을 제공할 수 있습니다. 예를 들어 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)
}

image image

이 부분에 대해 흥미가 있으시다면 webext-bridge의 onMessage에 대한 타입 정의를 살펴보세요. 문제가 있다면 저와 대화하실 수 있어요. 마지막으로 타입 별칭과 인터페이스에 대한 몇몇 사용 시나리오를 요약해보겠습니다.

언제 type을 사용할까요?

  1. 원시 타입에 별칭을 정의할 때는 type을 쓰세요
  2. 튜플 타입을 정의할 때 type을 쓰세요
  3. 함수 타입을 정의할 때 type을 쓰세요
  4. 유니온 타입을 정의할 때 type을 쓰세요
  5. 매핑된 타입을 정의할 때 type을 쓰세요

언제 interface을 사용할까요?

  1. 선언 병합 기능의 장점을 활용하려면 interface를 쓰세요
  2. 객체 타입을 정의하고 type을 사용할 필요가 없을 때 interface를 쓰세요

이 글을 읽고 여러분들이 타입 별칭과 인터페이스의 차이를 이미 이해하셨으리라 믿습니다.