TypeScript에서 타입은 조합할 수 있습니다. | 연산자는 여러 타입 중 하나를 허용하고, & 연산자는 여러 타입의 특성을 모두 합칩니다. 이 두 연산자를 이해하면 복잡한 데이터 구조도 정확하게 표현할 수 있습니다.
A | B는 A이거나 B인 타입입니다. 둘 중 하나면 됩니다.
type StringOrNumber = string | number;
function format(value: StringOrNumber): string {
if (typeof value === "string") {
return value.trim();
}
return value.toFixed(2);
}
format(" 안녕 "); // "안녕"
format(3.14159); // "3.14"
유니온 타입의 값을 사용하기 전에는 실제 타입이 무엇인지 확인해야 합니다. TypeScript는 확인하지 않으면 공통 메서드만 허용합니다.
function print(value: string | number) {
value.toString(); // 정상 — string과 number 모두 toString을 가짐
value.toUpperCase(); // 오류 — number에는 toUpperCase가 없음
}
특정 값만 허용하는 패턴입니다. 문자열 열거형처럼 쓸 수 있습니다.
type Direction = "left" | "right" | "up" | "down";
type StatusCode = 200 | 400 | 401 | 403 | 404 | 500;
function move(direction: Direction) {
console.log(`${direction}으로 이동`);
}
move("left"); // 정상
move("back"); // 오류: '"back"'은 'Direction'에 할당할 수 없습니다
객체 유니온에서 공통 프로퍼티로 타입을 구별하는 패턴입니다. TypeScript가 해당 분기에서 정확한 타입을 알 수 있어 안전하게 사용할 수 있습니다.
type Circle = {
kind: "circle";
radius: number;
};
type Rectangle = {
kind: "rectangle";
width: number;
height: number;
};
type Shape = Circle | Rectangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2; // 여기서 shape는 Circle로 확정
case "rectangle":
return shape.width * shape.height; // 여기서 shape는 Rectangle로 확정
}
}
A & B는 A이면서 동시에 B인 타입입니다. 두 타입의 모든 프로퍼티를 가져야 합니다.
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;
const person: Person = {
name: "철수",
age: 30,
// 둘 다 있어야 함
};
교차 타입은 기존 타입에 프로퍼티를 추가하는 데 자주 쓰입니다.
type User = {
id: number;
name: string;
};
type UserWithTimestamp = User & {
createdAt: Date;
updatedAt: Date;
};
const user: UserWithTimestamp = {
id: 1,
name: "철수",
createdAt: new Date(),
updatedAt: new Date(),
};
함수 타입을 교차하면 오버로드처럼 동작합니다.
type Stringify = (value: number) => string;
type Parse = (value: string) => number;
type Converter = Stringify & Parse;
| 유니온 (A | B) | 교차 (A & B) | |
|---|---|---|
| 의미 | A이거나 B | A이면서 B |
| 요구 사항 | 하나만 만족 | 둘 다 만족 |
| 결과 | 공통 멤버만 사용 가능 | 모든 멤버 사용 가능 |
| 사용 목적 | 다양한 입력 허용 | 타입 합성 및 확장 |
type A = { a: string };
type B = { b: number };
type Union = A | B;
// { a: string } 또는 { b: number } — 공통 프로퍼티 없으면 아무것도 접근 불가
type Intersection = A & B;
// { a: string; b: number } — 둘 다 있어야 하고 둘 다 접근 가능
type ApiSuccess<T> = {
status: "success";
data: T;
};
type ApiError = {
status: "error";
message: string;
code: number;
};
type ApiResponse<T> = ApiSuccess<T> | ApiError;
function handleResponse<T>(response: ApiResponse<T>) {
if (response.status === "success") {
console.log(response.data); // ApiSuccess<T>로 확정
} else {
console.error(response.message); // ApiError로 확정
}
}
유니온 타입은 여러 가능성 중 하나를 표현하고, 교차 타입은 여러 타입의 특성을 하나로 합칩니다. 판별 유니온 패턴은 switch/if 분기에서 TypeScript가 정확한 타입을 추론할 수 있게 해주어, 실수 없이 각 케이스를 처리하는 데 큰 도움이 됩니다.