여러분은 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 |