본문 바로가기
2024~/React

[타입스크립트] 교차타입

by Or0i쿠 2025. 6. 1.

교차타입 : & 기호를 사용하여 여러 타입을 하나로 결합하는 타입

문법: TypeA & TypeB & TypeC ..

1. 기본적인 속성 결합

interface Identifiable { id: number; }

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

// Identifiable 타입과 Timestamped 타입을 교차하여 새로운 타입을 생성
type DataWithTimestamp = Identifiable & Timestamped;

// DataWithTimestamp 타입의 객체는 id, createdAt, updatedAt 속성을 모두 가져야 합니다.
const myData: DataWithTimestamp = {
  id: 123,
  createdAt: new Date(),
  updatedAt: new Date(),
  // id, createdAt, updatedAt 중 하나라도 없으면 타입 에러 발생
};

console.log(myData.id);
console.log(myData.createdAt);
console.log(myData.updatedAt);

 

2. 동일한 이름의 속성 처리

  • 동일한 속성 타입: 만약 동일한 이름의 속성이 같은 타입이라면, 결과 타입에서도 해당 속성은 그 타입을 가집니다.
interface HasNameString1 { name: string; }
interface HasNameString2 { name: string; }

type CombinedNameString = HasNameString1 & HasNameString2;
// 결과: { name: string; }

 

  • 호환 가능한 속성 타입 (Union 포함): 만약 동일한 이름의 속성 타입이 서로 호환 가능하다면 (예: 한쪽이 다른 쪽의 서브타입), 더 구체적인(좁은) 타입이 됩니다. Union 타입의 경우, 두 타입의 교집합이 됩니다.
interface HasValueStringOrNumber { value: string | number; }
interface HasValueNumberOrBoolean { value: number | boolean; }

// value 속성의 타입은 (string | number) & (number | boolean)
type CombinedValue = HasValueStringOrNumber & HasValueNumberOrBoolean;
// 결과: { value: number; } (두 Union 타입의 공통 부분인 number만 남음)

const example: CombinedValue = { value: 123 }; // value는 반드시 number여야 함
// const exampleError: CombinedValue = { value: "hello" }; // 에러! string은 number가 아님
  • 호환 불가능한 속성 타입 (Primitive): 만약 동일한 이름의 속성이 서로 호환 불가능한 원시 타입(primitive type)이라면, 결과 타입에서 해당 속성의 타입은 never가 됩니다. never 타입은 값이 절대 존재할 수 없음을 의미하므로, 이러한 객체를 생성하는 것은 불가능합니다.
interface HasValueNumber { value: number; }
interface HasValueString { value: string; }

// value 속성의 타입은 number & string
type ConflictValuePrimitive = HasValueNumber & HasValueString;
// 결과: { value: never; }

// const obj: ConflictValuePrimitive = { value: 123 }; // 에러! number는 never가 아님
// const obj: ConflictValuePrimitive = { value: "hello" }; // 에러! string은 never가 아님
// ConflictValuePrimitive 타입의 객체를 만들 수 있는 방법은 없습니다.
  • 호환 불가능한 속성 타입 (Non-primitive): 동일한 이름의 속성이 서로 호환 불가능한 객체 타입(또는 배열, 함수 등)이라면, 결과 타입에서 해당 속성의 타입은 never가 됩니다.
interface HasDetailsObject { details: { a: number } }
interface HasDetailsArray { details: number[] }

// details 속성의 타입은 { a: number } & number[]
type ConflictDetails = HasDetailsObject & HasDetailsArray;
// 결과: { details: never; }

// ConflictDetails 타입의 객체를 만들 수 있는 방법은 없습니다.

인터페이스 vs 교차 타입

교차 타입(&)은 인터페이스의 상속(extends)과 유사한 역할이지만 몇 가지 차이점이 있다.

extends

  • 주로 객체 타입을 확장할 때 사용
  • 동일한 이름의 속성이 있을 경우 = 기본적으로 서브타입 관계가 성립해야 하며 그렇지 않으면 에러가 발생
  • 동일한 이름의 함수 멤버는 오버로딩

&

  • 객체 타입뿐만 아니라 원시 타입
  • Union 타입 등 모든 타입을 결합할 수 있다.
  • 동일한 이름의 속성 충돌 시 위에서 설명한 규칙(never 또는 Union의 교집합)에 따라 처리
  • 기존 타입을 수정하지 않고 조합할 때 유연

대부분의 경우 객체 타입을 결합할 때 extends나 & 모두 사용할 수 있지만, 복잡한 타입 조합이나 유니온 타입과의 결합 시에는 &가 더 강력하고 유연

// extends 사용
interface A { p1: string; }
interface B { p2: number; }
interface C extends A, B {} // C는 p1, p2를 모두 가짐

// 교차 타입 사용
type D = A & B; // D는 p1, p2를 모두 가짐

const objC: C = { p1: "hi", p2: 123 };
const objD: D = { p1: "hi", p2: 123 };

 


교차 타입 vs 유니온 타입 

정반대의 개념

타입 기호 의미 결과 타입의 속성 충돌 처리 예시
교차 타입 & A 이고 B A의 모든 속성 + B의 모든 속성 호환 가능 시 조합, 불가능 시 never TypeA & TypeB
유니온 타입 | A 이거나 B A 또는 B 둘 중 하나의 속성 집합을 가질 수 있음 타입 가드 필요 (typeof, in, 등) TypeA | TypeB

.

 


문제 : 

 

문제은행 6강 교차타입 - HackMD

Product 타입:

hackmd.io