본문 바로가기
programming_kr/typescript

typescript type과 interface의 차이

by JSsunday 2021. 10. 30.
728x90

typescript를 공부하던 중 어떤 곳에는 type를 쓰고 어떤 곳에는 interface를 써서 정확히 어떤 차이점이 있는지 이해할 수 없었습니다. 그래서 이 부분에 대해 알아보았습니다.

 

interface AnimalInterface {
  species: string
  height: number
  weight: number
}

const tiger: AnimalInterface = {
  species: 'tiger',
  height: 200,
  weight: 300
}

type AnimalType = {
  species: string,
  height: number,
  weight: number
}

const lion: AnimalType = {
  species: 'lion',
  height: 180,
  weight: 400
}

위에서 볼 수 있는 것 처럼, interface는 타입과 마찬가지로 객체의 타입의 이름을 지정하는 또 다른 방법입니다.

 

확장하는 방법(상속)

interface AnimalInterface {
  species: string
  height: number
  weight: number
}

interface AfricaAnimal extends AnimalInterface {
  continent: string
}

interface는 extends로 확장합니다.

 

type AnimalType = {
  species: string,
  height: number,
  weight: number
}

type AfricaAnimal = AnimalInterface & {
  continent: string
}

type은 특수문자&로 확장합니다.

 

선언적 확장

interface에서 할 수 있는 대부분의 기능들은 type에서 가능하지만, 한 가지 중요한 차이점은 type은 새로운 속성을 추가하기 위해서 다시 같은 이름으로 선언할 수 없지만, interface는 항상 선언적 확장이 가능하다는 것입니다. 그 차이에 대한 예제가 아래 내용입니다.

interface Animal {
  weight: number;
}

interface Animal {
  height: number;
}

const tiger: Animal = {
  weight: 100,
  height: 200,
};
console.log(tiger);

type _Animal = {
  weight: number;
};

type _Animal = {//error : 식별자가 중복됨
  height: string;
};

 

interface는 객체에만 사용이 가능하다.

interface AnimalInterface {
  name: string
}

type AnimalType = {
  name: string
}

type AnimalOnlyString = string
type AnimalTypeNumber = number

interface X extends string {} // 불가능

interface는 리터럴타입으로 확장할 수 없습니다.

 

computed value의 사용

type names = 'firstName' | 'lastName'

type NameTypes = {
  [key in names]: string
}

const yc: NameTypes = { firstName: 'hi', lastName: 'yc' }

interface NameInterface {
  // error
  [key in names]: string
}

type은 가능하지만 interface는 불가능합니다.

 

성능을 위해서는 interface를 사용하는 것이 좋을까?

 

여러 type혹은 interface &하거나 extend할 때를 생각해보면 interface는 속성간 충돌을 해결하기 위해 단순한 객체 타입을 만듭니다. 왜냐하면 interface는 객체의 타입을 만들기 위한 것이고, 어차피 객체만 오기 때문에 단순히 합치기만 하면 되기 때문입니다. 하지만 type의 경우, 재귀적으로 순회하면서 속성을 merge하는데, 이 경우에 일부 never가 나오면서 제대로 머지가 안될 수 있습니다. interface와는 다르게, type은 원시 타입이 올수도 있으므로, 충돌이 나서 제대로 merge가 안되는 경우에는 never가 떨어지는데 아래 예제를 봅시다.

 

type type2 = { a: 1 } & { b: 2 } // 잘 머지됨
type type3 = { a: 1; b: 2 } & { b: 3 } // resolved to `never`

const t2: type2 = { a: 1, b: 2 } // good
const t3: type3 = { a: 1, b: 3 } // Type 'number' is not assignable to type 'never'.(2322)
const t3: type3 = { a: 1, b: 2 } // Type 'number' is not assignable to type 'never'.(2322)

따라서 type간 속성을 merge할 때는 주의를 필요로 합니다. 객체에서만 쓰는 용도라면, interface를 쓰는 것이 훨씬 좋습니다.

 

type t1 = {
  a: number
}

type t2 = t1 & {
  b: string
}

const typeSample: t2 = { a: 1, b: 2 } // error
// before(3.x): Type 'number' is not assignable to type 'string'.
// after(4.x): Type 'number' is not assignable to type 'string'.(2322) input.tsx(14, 5): The expected type comes from property 'b' which is declared here on type 't2'

interface들을 합성할 경우 이는 캐시가 되지만, type의 경우에는 그렇지 않습니다.

type합성의 경우, 합성에 자체에 대한 유효성을 판단하기 전에, 모든 구성요소에 대한 type을 체크하므로 컴파일 시에 상대적으로 성능이 좋지않다고 합니다.

 

프로젝트를 설계하기 전에 type을 쓸지 interface를 쓸지 통일을 하면 좋을 것 같습니다. 그러나 객체, 그리고 type간의 합성등을 고려해 보았을 때 interface를 쓰는 것이 더 나을 것도 같네요. 공식문서에서도 특별한 경우를 제외하고는 type보단 interface를 사용하는 것이 더 좋다고 하네요.

728x90

댓글