"유니언이 여러 가능성 가운데 하나라면, 인터섹션은 여러 요구를 한 값이 모두 지켜야 한다는 뜻이야."
A이면서 B
A & B는 A의 멤버와 B의 멤버를 모두 가진 값을 나타내. 객체 타입에서는 여러 작은 계약을 조합해 하나의 큰 모양을 만들 수 있어. User & Timestamped라면 사용자 필드와 생성·수정 시각을 모두 가져야 해.
순서는 의미에 영향을 주지 않지만 같은 이름의 속성이 서로 호환되지 않으면 문제가 생겨. { name: string } & { name: number }의 name은 string이면서 number여야 하므로 사실상 never가 돼. 타입 선언 자체는 남아도 실제 값을 만들 수 없는 모양이 된다.
조합과 상속의 차이
interface의 extends도 객체 계약을 합칠 수 있어. 이름 있는 객체 계층을 열어 확장하려면 interface가 잘 읽히고, 유니언·조건부 타입을 포함한 임의의 타입 조각을 결합하려면 인터섹션이 더 유연해. 어느 쪽이 더 객체지향적인지보다 관계가 어떻게 읽히는지가 중요해.
교차 관심사를 붙여
식별 가능, 감사 기록, 시간 정보처럼 여러 도메인 객체에 선택적으로 붙는 작은 능력을 별도 타입으로 만들고 인터섹션으로 조합할 수 있어. 거대한 기본 인터페이스 하나에 모든 선택 속성을 넣는 것보다 각 계약의 경계가 선명해진다.
브랜드 타입의 밑바탕
type UserId = number & { __brand: 'UserId' }처럼 원시 타입과 고유 표식을 교차하면 일반 number와 구별되는 ID 타입을 만들 수 있어. 실행 중에는 숫자지만 컴파일러는 생성 함수를 거쳐 얻은 UserId만 받게 할 수 있지.
브랜드는 단언을 아무 데나 붙이면 무너져. 검증하거나 생성하는 한정된 함수가 브랜드를 부여해야 의미가 있어. 타입 트릭이 도메인 경계를 대신하는 게 아니라 경계를 표현하는 도구라는 점을 잊지 마.