숏컷 코드
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;교차 조합
intersection에서 먼저 보는 기본형은 아래입니다.
- 객체 조각 합치기:
A & B - 공통 속성 + 역할 조합:
Entity & Timestamped - 같은 키가 호환되면 유지, 충돌하면
never - 충돌 키로
never가 되는 경우 interface extends와의 선택 비교
1) 여러 능력을 동시에 갖춘 타입이면 intersection
독립적인 타입 조각을 하나의 구체 타입으로 합치고 싶을 때 intersection이 자연스럽습니다.
type WithName = { name: string };
type WithRole = { role: "admin" | "user" };
type AdminUser = WithName & WithRole;즉 "둘 중 하나"가 아니라 "둘 다 포함"이 핵심입니다.
2) 객체 타입에선 프로퍼티가 합쳐진다
객체 타입끼리의 intersection은 프로퍼티를 병합한 결과처럼 읽을 수 있습니다.
type A = { x: number };
type B = { y: string };
type C = A & B;그래서 타입 조각을 조합하는 용도로 자주 쓰입니다.
type Entity = { id: string };
type Timestamped = { createdAt: Date };
type SavedEntity = Entity & Timestamped;공통 키가 같은 방향이면 그대로 유지됩니다.
type HasId = { id: string };
type WithName = { id: string; name: string };
type User = HasId & WithName; // { id: string; name: string }3) 충돌 키는 오히려 위험 신호다
같은 키가 양쪽 타입에 서로 다른 타입으로 존재하면, 결과는 그 교집합이 되어 실질적으로 never에 가까워질 수 있습니다.
type X = { id: string };
type Y = { id: number };
type Z = X & Y;이런 타입은 선언은 되지만 사용 시점에서 거의 할당 불가능한 형태가 됩니다.
4) intersection은 런타임 merge가 아니라 타입 조합이다
A & B를 썼다고 객체가 자동으로 합쳐지는 것은 아닙니다.
실제 값은 spread, Object.assign, 함수 반환값 조합 같은 런타임 코드로 만들어야 합니다.
type A = { x: number };
type B = { y: string };
type C = A & B;
const value: C = { ...{ x: 1 }, ...{ y: "ok" } };즉 intersection은 데이터 조합 방법이 아니라 "결과 타입이 동시에 만족해야 할 계약"입니다.
5) interface extends와는 충돌 처리 감각이 다르다
interface extends는 충돌을 더 일찍 드러내는 쪽이고, intersection은 더 유연하지만 문제를 사용 시점까지 끌고 갈 수 있습니다.
즉 설계 의도에 따라 더 명시적인 쪽을 고르면 됩니다.
- 선언 단계에서 충돌을 바로 보고 싶다:
interface extends - 타입 조각을 조합해 빠르게 만든다: intersection
6) 원시 타입 intersection은 대체로 의도를 다시 봐야 한다
string & number 같은 조합은 동시에 만족할 값이 없으므로 사실상 never가 됩니다.
그래서 intersection은 주로 객체 조합 맥락에서 보는 편이 자연스럽습니다.
언제 교차 타입을 쓸까
- "A 또는 B"다: union
A | B - "A이면서 B"다: intersection
A & B - 여러 타입 조각을 합친다: intersection
- 결과 타입 계약만 합친다: 런타임 merge는 별도
- 충돌 키가 있다: 결과가
never로 무너지는지 점검 - 조기 오류가 더 좋다:
interface extends도 검토
주의할 점
같은 키가 양쪽 타입에서 다른 타입으로 선언되면 intersection 결과는 사실상 사용 불가능한 타입이 될 수 있습니다.
type A = { id: string };
type B = { id: number };
type C = A & B;
// C.id는 string과 number를 동시에 만족해야 하므로 사실상 never참고 링크
1 sources