typeScript 로 작업을 하다가 둘의 정확한 차이는 무엇인가,
타입으로 할 때는 오류가 나고 인터페이스로 할 때는 오류가 안나는 경우가 생기면서
둘의 차이점을 파헤쳐 보기로 했다.
일단 일반적으로 interface 와 type 차이점을 검색하면 나오는 내용들은 비슷하다.
interface는 객체에서만 사용이 가능하다.
computed value 사용은 type만 가능
type names = 'firstName' | 'lastName'
type NameTypes = {
[key in names]: string
}
const test: NameTypes = { firstName: 'su', lastName: 'lee' }
interface NameInterface {
// error
[key in names]: string
}
확장의 방법
Interface 는 extends 를 통해 선언적 확장이 가능하다. 즉, 동일한 이름으로 interface를 선언하는 것이 가능하다.
type 은 & 를 통해 타입을 확장한다.
내가 궁금한 점은 확장에서의 차이점이었다.
interface IBase {
prop_a: string;
prop_b?: string;
proc_c: string;
}
interface IExtendBase extends IBase {
prop_a?: string;
prop_b: string;
prop_new: string;
}
먼저 interface에서의 확장을 해보자
이렇게 작성했을 경우
이러한 에러메세지가 뜬다.
이 에러가 나오는 이유는, prop_a
는 IExtendBase
의 prop_a
를 덮어쓰지 못하기 때문이다.
IExtendBase
에 있는 props_a
는 옵셔널이기 때문에 IBase
에 있는 prop_a
보다 더 넓은 범위의 타입을 갖는다. 그렇기 때문에 IBase
의 string
값만 갖는 prop_a
는 확장을 하지 못해 string 형식에 할당할 수 없다는 에러가 난다.
비슷한 사례로 또 다른 예시를 들어보면,
interface IExtendBase extends IBase {
prop_a: string | number;
prop_b: string;
prop_new: string;
}
IExtendBase
의 prop_a
가 string | number
를 갖는데, IBase
는 string
값만 갖기 때문에 형식에 할당할 수 없다는 에러가 나온다.
그렇다면 prop_a 의 타입을 변경해주고 싶을 땐 어떻게 해야 하나..?
interface IExtendBase extends Omit<IBase, "prop_a"> {
prop_a?: string;
prop_b: string;
prop_new: string;
}
omit 을 사용해서 해결해 줄 수 있다 !
직접 타입을 보면
type Test<T> = {
[k in keyof T]: T[k];
};
type test = Test<IExtendBase>;
옵셔널을 붙여준 prop_a
타입이 잘 들어간 것을 볼 수 있다.
type도 똑같이 작성해 보자
type TBase = {
prop_a: string;
prop_b?: string;
prop_c: string;
};
type TExtendBase = TBase & {
prop_a?: string;
prop_b: string;
prop_new: string;
};
어라라 인터페이스와 같이 작성했음에도 불구하고 에러가 나지 않는다 !
그리고 타입을 보니 옵셔널은 되지 않고 string
만 남아있는 것을 볼 수 있다.
왜 타입은 되는 걸까?
타입의 확장은 두 타입에 포함된 모든 속성을 가진 새 타입을 생성하기 때문이다. 교집합 만을 가진다고 생각하면 된다.
예를 들어 TExtendBase
의 prop_a
가 더 넓은 값(string|number
)의 타입을 가질 경우, TBase
의 string
타입만을 가진다.
type TExtendBase = TBase & {
prop_a: string | number;
prop_b: string;
prop_new: string;
};
(string 만을 가지는 것을 볼 수 있음 )
다이어그램 이미지를 통해 더 쉽게 이해할 수 있다.
TypeA 에서 prop_a:string | boolean
, TypeB에서 prop_a: boolean | number
인데 이걸 합친 TypeC는 교집합인 prop_a
에 boolean
값만을 가지는 것을 볼 수 있다.
확장한 타입에서 prop_a 의 타입을 더 확장해주고 싶을 경우에는 어떻게 해줘야 할까? 인터페이스와 비슷하게 omit을 통해 해줄 수가 있다.
type TBase = {
prop_a: string;
prop_b?: string;
prop_c: string;
};
type TExtendBase = Omit<TBase, "prop_a"> & {
prop_a?: string;
prop_b: string;
prop_new: string;
};
prop_a 를 옵셔널로 만들어 주고자 omit으로 prop_a의 결합을 빼고, 새로운 타입을 입력하여 옵셔널로 확장할 수 있다.
둘의 확장에 대한 차이에 대해 알아 보았는데, 궁금했던 점이 좀 해결되었다..!
출처 : https://smnh.me/extending-typescript-interfaces-and-type-aliases-with-common-properties