개발새발
타입을 집합으로 생각하자 (유니언, 인터섹션) 본문
TypeScript는 타입을 집합(set) 처럼 다룬다. 집합 이론의 관점에서 타입 연산자를 해석하면, 타입 시스템을 더 명확하게 이해할 수 있고, 복잡한 타입도 직관적으로 설계할 수 있다.
이번 글에서는 | (유니언), & (인터섹션), 그리고 unknown, never 등의 특수 타입을 집합 개념으로 설명하겠다.
유니언 타입: |는 합집합
유니언(union)은 둘 중 하나라도 만족하는 값을 의미한다. 수학적 개념으로는 합집합이다.
let strOrNum: string | number = 'hello';
strOrNum = 123;
타입 string | number는 문자열이거나 숫자인 값을 모두 포함하는 집합이다.
인터섹션 타입: &는 교집합
인터섹션(intersection)은 두 타입을 모두 만족하는 값을 의미한다. 수학적으로는 교집합이다.
type NeverType = string & number; // ❌
여기서 string이면서 동시에 number인 값은 존재할 수 없다. 따라서 이 타입은 never, 즉 공집합이다.
+ never는 값이 절대 존재할 수 없는 타입이다!
타입의 전체집합과 공집합
개념 | TypeScript | 타입 의미 |
전체집합 | unknown | 모든 타입의 최상위 타입 |
공집합 | never | 어떤 값도 가질 수 없는 타입 |
const a: unknown = 123; // ✅ 모든 타입 가능
const b: never = 123; // ❌ Error
- unknown은 어떤 값도 받을 수 있다.
- never는 어떤 값도 가질 수 없다.
대입 관계 (서브타입 관계)
항상 좁은 타입은 넓은 타입에 대입 가능하지만, 그 반대는 안 된다.
let a: unknown;
a = 'hello'; // ✅ string → unknown
let b: string;
// b = a; // ❌ unknown → string (안전하지 않음)
- never ⊆ 모든 타입
- 모든 타입 ⊆ unknown
- 따라서: never → 모든 타입 → unknown 방향으로만 대입 가능
유니언 & 인터섹션 예제 정리
type A = string | boolean; // string 또는 boolean
type B = boolean | number; // boolean 또는 number
type C = A & B; // A와 B의 교집합 → boolean
type D = {} & (string | null); // string
type E = string & boolean; // never
type F = unknown | {}; // unknown
type G = never & {}; // never
- C: A와 B의 공통 타입은 boolean
- D: {}는 null/undefined를 제외한 값이므로 string과 교집합 가능
- E: string과 boolean은 공통 값이 없으므로 never
- F: unknown과 어떤 타입의 합집합은 항상 unknown
- G: 공집합과의 교집합은 항상 never
예외적인 교집합 – 객체와 원시값
일반적으로 객체와 원시값의 교집합은 never 여야 하지만, 예외도 있다.
type H = { a: 'b' } & number; // { a: 'b' } & number
type I = null & { a: 'b' }; // never
type J = {} & string; // string
주의할 점
- H는 never가 아닙니다. TypeScript에서는 브랜딩 기법을 위해 이런 형태를 허용한다.
- J가 string인 이유는 {}는 null/undefined를 제외한 모든 값을 포함하기 때문이다.
실무에서의 활용 팁
✅ 유니언으로 여러 가능성 명시
type ResponseStatus = 'success' | 'fail' | 'loading';
✅ 인터섹션으로 여러 타입 결합
type WithID = { id: string };
type WithTimestamp = { createdAt: Date };
type Entity = WithID & WithTimestamp;
const user: Entity = {
id: 'abc',
createdAt: new Date(),
};
✅ 조건부 타입과 함께 사용
type Flatten<T> = T extends any[] ? T[number] : T;
type A = Flatten<string[]>; // string
type B = Flatten<number>; // number
연산자 / 키워드 | 의미 | 예시 결과 |
` | ` | 합집합 (or) |
& | 교집합 (and) | string & boolean → never |
never | 공집합 | 값 없음 |
unknown | 전체집합 | 모든 타입 포함 |
any | 특수 케이스 | 일관성 없음, 지양 |
'Typescript' 카테고리의 다른 글
객체 간에 대입할 수 있는지 확인하는 법을 배우자 (0) | 2025.03.22 |
---|---|
타입도 상속이 가능하다 (0) | 2025.03.22 |
객체의 속성과 메서드에 적용되는 특징을 알자 (0) | 2025.03.22 |
인터페이스로 객체를 타이핑하자 (0) | 2025.03.16 |
타입 별칭으로 타입에 이름을 붙이자 (0) | 2025.03.14 |