반응형
250x250
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

짧은코딩

type과 interface 본문

TS/이펙티브 타입스크립트

type과 interface

5_hyun 2023. 2. 21. 14:17

타입스크립트에서 명명된 타입(named type)을 정의하는 방법은 type과 interface 2가지가 있다. 대부분의 경우 type과 interface 둘 중 아무거나 사용해도 문제가 없다. 하지만 둘 중 하나로 통일해야 하는 상황이 있다. 이 글에서는 type과 interface의 공통점과 차이점에 대해서 알아보도록 하자.

그 전에 이 글에서 interface는 IState, type는 TState로 표기하는 일이 있을텐데 이 방법은 좋은 방법은 아니라서 지양하는 것이 좋다. 예시 코드에는 저 방식을 사용하여서 불가피하게 저 방식을 예시로 사용하겠다.

type과 interface의 공통점

1. 인덱스 시그니처와 함수 타입

인덱스 시그니처와 함수 타입은 type과 interface에서 모두 사용된다.

// 인덱스 시그니처
type TDict = { [key: string]: string };
interface IDict {
    [key: string]: string;
}

// 함수 타입
type TFn = (x: number) => string;
interface IFn {
    (x: number): string;
}

const toStrT: TFn = x => "" + x; // 정상
const toStrI: IFn = x => "" + x; // 정상

단순한 함수 타입에서는 type이 더 나은 선택이지만 interface를 사용해도 문제는 없다.

 

2. 제네릭

type과 interface 모두 제네릭이 가능하다.

type TPair<T> = {
    first: T;
    second: T;
}
interface IPair<T> = {
    first: T;
    second: T;
}

 

3. 인터페이스는 타입을 확장, 타입은 인터페이스를 확장

interface IStateWithPop extends TState {
    population: number;
}
type TStateWithPop = IState & { population: number };

IStateWithPop와 TStateWithPop는 동일하다.

주의할 점은 interface는 유니온 타입 같은 복잡한 타입을 확장하지 못한다. 복잡한 타입을 확장하고 싶으면 타입과 "&"를 사용해야 한다.

 

한편 클래스에서는 implements을 사용해 타입과 인터페이스 모두 확장할 수 있다.

type TState = {
  name: string
  capital: string
}
interface IState {
  name: string
  capital: string
}
class StateT implements TState {
  name: string = ''
  capital: string = ''
}
class StateI implements IState {
  name: string = ''
  capital: string = ''
}

type과 interface의 차이점

1. 유니온

유니온 타입은 있지만 유니온 인터페이스라는 개념은 없다. 인터페이스는 타입 확장을 할 수 있지만 유니온 할 수는 없다.

하지만 유니온 타입을 확장하는 것이 필요할 때가 있다.

type Input = {
  /* ... */
}
type Output = {
  /* ... */
}
interface VariableMap {
  [name: string]: Input | Output
}

Input, Output는 별도의 타입이고 이 두 타입을 하나의 변수명으로 매핑하는 VariableMap 인터페이스를 만들 수 있다.

type NamedVariable = (Input | Output) & { name: string };

이 타입은 인터페이스로 표현할 수 없다. type은 유니온이 될 수 있고 매핑된 타입 또는 조건부 타입 같은 고급 기능에 활용되어 interface보다 쓰임새가 많다.

 

2. 튜플, 배열

튜플이나 배열 타입도 type을 이용하면 간결하게 표현할 수 있다.

 

-type

type Pair = [number, number]
type StringList = string[]
type NamedNums = [string, ...number[]]

이런식으로 하면된다.

 

-interface

interface Tuple {
  0: number
  1: number
  length: 2
}
const t: Tuple = [10, 20] // OK

물론 interface로도 구현이 가능하지만 튜플에서 사용하는 concat 같은 메서드를 사용할 수 없다.

 

3. 선언 병합(declaration merging) (보강)

interface IState {
  name: string
  capital: string
}
interface IState {
  population: number
}
const wyoming: IState = {
  name: 'Wyoming',
  capital: 'Cheyenne',
  population: 500_000,
} // OK

interface는 위 코드처럼 속성을 확장할 수 있고 이를 선언 병합이라고 부른다.

결론

1. type과 interface를 모두 사용할 수 있다면 일관성과 보강의 관점에서 고려를 해야 한다.

2. 일관된 타입을 사용한다면 type, 어떤 API에 대한 타입을 선언하거나 타입 확장이 필요하면 병합이 가능한 interface를 사용하는 것이 좋다.

3. 상황에 따라 정하는 것이 좋지만 대부분은 interface를 사용하는 것이 좋다.

728x90
반응형
Comments