타입스크립트에서 인터섹션 타입과 유니온 타입을 배우면서 겪었던 어려움으로
인터섹션(&) 타입은 교집합으로 두 타입간의 공통적인 타입을 만들어내고
유니온(|) 타입은 합집합으로 두 타입을 모두 포함하는 타입을 만들어낸다고 배웠다.
인터섹션 타입은
type foo = {
x: number;
}
type bar = {
y: number;
}
foo & bar
를 사용해서 새로운 타입을 만들어 낸다면
두 타입의 교집합
이기 때문에 공통된 속성이 하나도 없으므로 공집합이 될 것이라 생각했다.
그러나,
만들어지는 타입은
type newType = {
x: number;
y: number;
}
마치 합집합 처럼 보인다. 두 타입의 공통된 속성이 아니라 두 타입의 모든 속성이 모두 포함되어 있는 새로운 타입이 만들어진다.
그래서 처음에는 이게 어떻게 교집합인가? 내가 집합에 대해서 잘못알고 있었나 집합부터 다시 배워야하나...
그 당시에는 일단 실무에서 사용해야 하고 이해하고 넘어가야 하기 때문에 내가 노션에 정리한걸 살펴보니 "타입을 집합으로 이해하지 말자" 라고 적어놨다.
그러다 (번역) 타입스크립트에 대한 다른 접근법 벨로그 게시글을 보고
집합으로 이해할 수 있다는 것을 알게 되어서
타입스크립트를 집합으로 이해하는 것을 포기하지 말고 제대로 정리해보려고 한다.
& 를 사용한 인터섹션 타입은 “교집합”을 의미한다.
그렇다면
type Foo = { x: number };
type Bar = { y: number };
type IT = Foo & Bar;
새로 만든 IT 라는 타입은 Foo 타입과 Bar 타입의 교집합이기 때문에
공집합이 되어 아무런 타입도 가질 수 없게 되고
const test:IT = {
x: 10
}
이렇게 사용한다면 타입 에러가 발생해야 한다는게 집합으로 타입을 생각할 때 발생하는 일이 였다.
그러나 실제로 사용할 때는
const test:IT = {
x: 10,
y: 20,
}
const test2:IT = { // 타입 에러
x: 10,
}
const test3:IT = { // 타입 에러
y: 20,
}
객체 속성으로 x만 있어서는 안되며 y만 있어서도 안되고
x와 y가 반드시 같이 있어야 한다.
💡 마치 합집합 처럼 동작한다.
그렇기 때문에 타입을 집합으로 생각한다면 공집합이여야 하지만 실제로는 합집합이 되어 버리기 때문에
타입으로 생각하지 말자고 했던 것이다.
그러다 타입에 대한 새로운 접근법을 이해하게 되었는데
이게 일반적으로 이해하는 집합의 개념으로 타입을 바라본 것이다.
하지만 실질적으로
type Bar = { y: number };
Bar 라는 타입은 number 타입의 y를 갖고 있는 타입만을 의미하는게 아니라
최소한 number 타입의 y를 갖고 있는 객체 리터럴의 무한 집합이다.
const barTest: Bar = {
y: 20,
z: 200,
}
물론 이건 안된다.
Bar 타입이 number 타입의 y를 갖고 있는 객체 리터럴의 무한 집합이라는걸 확인하기 위해서
type Baz = {
y: number;
}
type NES = {
y: number;
foo: string;
z: number;
}
const nes: NES = {
y: 10,
foo: "hello",
z: 200,
}
const barTest:Baz = nes;
이 코드가 통과하는 것을 볼 수 있는데, NES 타입은 y(number), foo(string), z(number) 속성을 갖고 있다.
이 타입의 객체를 Baz 타입 변수에 대입 했는데 타입 에러가 발생하지 않는다.
즉, y 속성이 일치한다면(타입) Baz 타입의 변수 barTest에 nes 값을 넣어도 타입 에러가 발생하지 않는 것이다.
여기까지 이해했다면
인터섹션 타입을 다시 바라보자
type Foo = { x: number };
type Bar = { y: number };
type IT = Foo & Bar;
Foo 타입은 number 타입의 x 속성을 갖고 있는 객체 리터럴의 무한 집합이고
Bar 타입은 number 타입의 y 속성을 갖고 있는 객체 리터럴의 무한 집합이다.
그렇다면 두 타입의 교집합은
number 타입의 x 속성을 갖고 있고 number 타입의 y 속성을 갖고 있는 객체 리터럴의 무한 집합이된다.
따라서
const test:IT = {
x: 10,
y: 20,
}
const test2:IT = { // 타입 에러
x: 10,
}
const test3:IT = { // 타입 에러
y: 20,
}
두 타입의 인터섹션 타입으로 만든 변수에 x,y 속성이 (number) 반드시 존재해야 하고
x 속성만 있는것은 안되며 y 속성만 있는것도 안된다.
교집합은 마치 합집합 처럼 동작했고
내부적인 흐름을 확인하고 나서 원리를 이해했다
그렇다면 합집합은 어떻게 될까?
type Foo = { x: number };
type Bar = { y: number };
type UT = Foo | Bar;
이전에 설명했듯이 Foo는 number 타입의 x 속성을 갖고 있는 객체 리터럴의 무한이고
Bar는 number 타입의 y 속성을 갖고 있는 객체 리터럴의 무한이다.
따라서 Foo | Bar 를 그림으로 이해하면
코드로 보면,
const test1:UT = {
x: 10,
y: 20,
}
const test3: UT = {
x: 10,
}
const test4: UT = {
y: 10,
}
모든 변수에서 타입 에러는 발생하지 않는다.