Typescript

Type 지정 뭐 사용하시나요?

아가프린 2024. 3. 27. 09:40

여러분은 TypeScript를 사용하실 때 타입 지정을 뭘로 하시나요? TypeScript에는 크게 2가지로 타입을 명시할 수 있습니다.

interface와 type 으로 말이죠. 근데 사실상 타입 지정만 생각한다면 둘은 어떤 차이가 있는지 모를 수 있습니다.

둘은 비슷하면서도 아주 다릅니다! 평소 많이 사용하면서 어떤 때에 적절히 사용하는 것이 좋을까 생각해 작성해 보았습니다.

 

interface

첫 번째 키워드 interface, interface는 타입을 지정할 수 있지만 객체의 타입을 지정합니다.

객체가 아닌 다른 곳에는 사용을 못하죠.

interface UserType {
  name: string;
  age: number;
  isAdult: boolean;
}

const user: UserType = {
  name: '이운린',
  age: '19',
  isAdult: false,
};

 

위와 같이 사용합니다.

 

type

type도 TypeScript에서 타입을 지정할 수 있는 방법 중 하나입니다. 하지만 type은 객체 뿐만 아니라 다른 곳에도 사용이 가능합니다.

type colorType = 'red' | 'yellow';

type UserType = {
  name: string;
  age: number;
  isAdult: boolean;
};

let color: colorType = 'red';

const user: UserType = {
  name: '이운린',
  age: 19,
  isAdult: false,
};

 

벌써 차이점이 좀 보이죠? 사용 방식이나 범위부터 다릅니다.

type은 객체 뿐 아니라 모든 타입을 정의할 수 있습니다. ex) 유니온, 튜플, 함수 등

 

차이점

위에서 간단하게 둘의 차이를 보여드렸습니다. 이제 더 상세하게 차이점에 대해 알아보겠습니다.

 

1. 상속

 

첫 번째로 타입을 상속하는 방법입니다. TypeScript는 class와 비슷하게 다른 타입을 상속받을 수 있습니다.

여기서 interface와 type의 차이점이 나타나는데요.

 

interface UserType {
  name: string;
  age: string;
  isAdult: boolean;
}

interface PremiumUserType extends UserType {
  // name: string;
  // age: string;
  // isAdult: boolean;
  money: number;
}

 

interface의 확장 방법입니다. 기존 class 문법과 비슷하게 extends를 사용하여 타입을 상속 받습니다.

UserType을 상속받은 PremiumUserType은 UserType의 요소들을 가지고 있습니다.

 

type UserType = {
  name: string;
  age: string;
  isAdult: boolean;
}

type PremiumUserType = UserType & {
  // name: string;
  // age: string;
  // isAdult: boolean;
  money: number;
}

 

위 코드가 type의 상속 방법입니다. UserType의 타입들 (& = 교집합)을 가져옵니다. 그리고 새로운 타입을 추가합니다!

 

개발자의 취향으로 많이 나뉘게 됩니다. 저는 type의 상속보다는 interface의 상속을 더 선호합니다.

type은 여러번 상속하게 되면 interface 보다 비교적 알아보기 어려워지기 때문입니다.

 

2. 선언적 확장

 

지금까지만 보면 interface에서 할 수 있는 건 거의 type에서 할 수 있습니다. 좀 더 범위가 크기도 하고요.

그러나 interface만의 기능도 있습니다. 바로 새로운 타입을 추가하기 위해 같은 이름을 다시 선언할 수 있습니다.

UserType을 위에서 선언했어도 다시 선언해 확장할 수 있다는 것입니다. type은 다시 선언한다면 오류를 발생시킵니다.

interface UserType {
  name: string;
  age: number;
  isAdult: boolean;
}

interface UserType {
  subscribe: boolean;
}

const user: UserType = { // 마지막으로 선언된 이름의 타입이 지정됩니다.
  name: '이운린',
  age: '19',
  isAdult: fasle,
  subscribe: true,
};

 

여기서 굳이 왜 2번 선언하지라는 생각이 들 수 있습니다. 하지만 이렇게 여러 interface를 정의해 재사용성을 높일 수 있습니다.

여러개의 동일한 속성을 갖고 있다면 이것만 따로 interface로 정의해두면 여러 interface에서 똑같은 이름으로 새로운 타입들을 추가할 수 있습니다. 또 어떤 속성이나 메서드를 갖고 있는지 명시적으로 표현할 수도 있습니다.

 

3. computed value

 

TypeScript에는 computed value라는 것이 있습니다. interface는 computed value를 사용하면 안 됩니다.

type Color = 'red' | 'yellow' | 'blue';

interface Art {
  [key in Color]: string;
}

 

이렇게 사용하면

TypeScript는 A mapped type may not declare properties or methods.

 

TypeScript는 다음과 같은 오류를 보냅니다. 하지만 type을 이용하면 computedValue를 이용할 수 있습니다.

type Color = 'red' | 'yellow' | 'blue';

type Grades = {
  [key in Color]: string;
}

 

4. 성능

 

사실 이 글의 주제를 인터넷에 검색하면 interface가 type보다 성능이 좋다. 라는 말이 많습니다.

이런 이유는 type과 interface의 속성 간 충돌 때문이 큽니다. interface는 객체만이 오기 때문에 속성의 충돌을 해결합니다.

 

하지만 type은 위에서 봤던 것처럼 원시 타입들도 정의할 수 있습니다. type은 재귀적으로 순회하며

속성을 머지하는데 원시 타입이 올 수 있으므로 충돌이 발생해 제대로 머지가 발생이 안 되는 경우에 never로 처리하는

경우들이 생기게 됩니다. 그래서 공식문서에서도 type말고 interface로 객체의 타입을 명시하라고 되어 있습니다.

 

type Type1 = { name: string } & { age: number } // success
type Type2 = { name: string, age: number } & { age: string } // never

const ex1: Type1 = { name: '이운린', age: 19 }
const ex2: Type2 = { name: '이운린', age: 19 } // Type 'number' is not assignable to type 'never'
const ex3: Type2 = { name: '이운린', age: '19' } // Type 'string' is not assignable to type 'never'

 

never로 판단해서 어떤 값이든 age에는 무엇도 할당할 수 없게 됩니다. 이제는 type도 어디서 오류가 발생했는지 알려주지만

웬만하면 객체는 interface를 사용하는 것이 좋을 것 같습니다 :)

'Typescript' 카테고리의 다른 글

TypeScript - 우리가 unknown을 써야하는 이유  (0) 2024.08.11
Typescript 왜 쓰는거지 🤔  (1) 2024.02.11