ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템 13 - 타입과 인터페이스의 차이점 알기
    타입스크립트 2023. 3. 7. 09:28

    타입스크립트에서 명명된 타입을 정의하는 방법은 두 가지가 있다.

    1. 타입을 사용하는 방법

      type TState = {
        name: string;
        capital: string;
      }
    2. 인터페이스를 사용하는 방법

      interface IState {
        name: string;
        capital: string;
      }

    대부분의 경우에는 타입을 사용해도 되고 인터페이스를 사용해도 되는데, 타입과 인터페이스 사이에 존재하는 차이를 분명하게 인식함으로써 같은 상황에서 동일한 방법으로 명명된 타입을 정의해 코드의 일관성을 유지할 수 있도록 해야한다.

    인터페이스 선언과 타입 선언의 비슷한 점

    • 잉여 속성 체크할 수 있다.

      // type 키워드를 사용할 경우
      type TState = {
        name: string;
        capital: string;
      }
      const wyoming: TState = {
        name: 'Wyoming',
        capital: 'Cheyenne',
        population: 500_000
      // ~~~~~~~~~~~~~~~~~~ Type ... is not assignable to type 'TState'
      //                    Object literal may only specify known properties, and
      //                    'population' does not exist in type 'TState'
      };
      
      // 인터페이스를 사용할 경우
      interface IState {
        name: string;
        capital: string;
      }
      const wyoming: IState = {
        name: 'Wyoming',
        capital: 'Cheyenne',
        population: 500_000
      // ~~~~~~~~~~~~~~~~~~ Type ... is not assignable to type 'IState'
      //                    Object literal may only specify known properties, and
      //                    'population' does not exist in type 'IState'
      };
    • 인덱스 시그니처를 사용할 수 있다.

      type TDict = { [key: string]: string };
      interface IDict {
        [key: string]: string;
      }
    • 함수 시그니처를 만들 수 있다.

      // type 키워드를 사용할 경우
      type TFn = (x: number) => string;
      const toStrT: TFn = (x) => '' + x;  // OK
      
      // 인터페이스를 사용할 경우
      interface IFn {
        (x: number): string;
      }
      const toStrI: IFn = (x) => '' + x;  // OK
    • 제네릭을 사용할 수 있다.

      type TPair<T> = {
        first: T;
        second: T;
      }
      interface IPair<T> {
        first: T;
        second: T;
      }
    • 타입을 확장할 수 있다. (단, 인터페이스는 유니온 타입 같은 복잡한 타입은 확장 불가능하다.)

      // type 키워드를 사용할 경우
      type TState = {
        name: string;
        capital: string;
      }
      type TStateWithPop = IState & { population: number; };
      
      // 인터페이스를 사용할 경우
      interface IState {
        name: string;
        capital: string;
      }
      interface IStateWithPop extends IState {
        population: number;
      }
    • 클래스를 구현(implements)할 수 있다.

      // type 키워드를 사용할 경우
      type TState = {
        name: string;
        capital: string;
      }
      class StateT implements TState {
        name: string = '';
        capital: string = '';
      }
      
      // 인터페이스를 사용할 경우
      interface IState {
        name: string;
        capital: string;
      }
      class StateI implements IState {
        name: string = '';
        capita: string = '';
      }
      

    인터페이스 선언과 타입 선언의 차이점

    • 유니온 타입은 존재하지만 유니온 인터페이스는 존재하지 않는다.

      type AorB = 'a' | 'b';
    • 인터페이스는 타입을 확장할 수 있지만, 인터페이스의 프로퍼티의 타입 중 유니온 타입은 확장할 수 없다.

      type Input = { /* ... */ };
      type Output = { /* ... */ };
      
      type VariableMap = Input | Output;
      interface VariableMap {
        [name: string]: Input | Output;
      }
      
      // 만약 Input 혹은 Output 타입에 name이라는 프로퍼티가 추가된 타입을 만들고 싶다면 type 키워드로 다음과 같이 만들 수 있지만 인터페이스로는 만들 수 없다.
      type NamedVariable = (Input | Output) & { name: string };
    • 튜플과 같은 배열 타입을 타입과 인터페이스로 선언할 수 있지만, 인터페이스로 선언한 튜플 타입은 배열에서 사용할 수 있는 concat 같은 메서드를 사용할 수 없다. 따라서 튜플은 type 키워드로 구현하는 것이 좋다.

      // type 키워드를 사용할 경우
      type Pair = [number, number];
      const t: Pair = [10, 20];  // OK
      t.concat(); // OK
      
      // 인터페이스를 사용할 경우
      interface Pair {
        0: number;
        1: number;
        length: 2;
      }
      const t: Pair = [10, 20];  // OK
      t.concat();
      // ~~~~~~~~~~~~~~~~~~ Property 'concat' does not exist on type 'Pair'
    • 인터페이스에서만 타입을 보강할 수 있다.

      // 이 예제처럼 속성을 확장하는 것을 선언 병합(declaration merging)이라고 한다.
      interface IState {
        name: string;
        capital: string;
      }
      interface IState {
        population: number;
      }
      const wyoming: IState = {
        name: 'Wyoming',
        capital: 'Cheyenne',
        population: 500_000
      };  // OK

    타입 선언 vs 인터페이스 선언

    • 복잡한 타입을 정의할 때는 인터페이스보다 type 키워드를 사용하는 것이 좋다.
    • 타입 선언과 인터페이스 두 가지 방법으로 모두 표현할 수 있고, 기존 프로젝트 스타일이 확립된 경우라면 일관성을 유지하는 것이 좋고, 프로젝트 스타일이 확립되지 않은 경우에는 보강의 가능성이 있는지 고려하여 타입을 선언하는 것이 좋다.

    댓글

Designed by Tistory.