하나이 함수에 대해서 첫번째로 입력받는 인자의 값에 따라 이후 인자의 타입을 결정할 수 있는 방법입니다. 함수 오버로딩이 아닌 TypeScript 본연의 타입 기능을 이용하는 방법으로 함수 오버로딩보다 더욱 작성하기 쉽고 똑똑합니다. 예를들어 emit라는 함수를 다음처럼 정의합니다.
type EventMap = {
click: { x: number; y: number };
hover: { elementId: string };
keyPress: { key: string; code: number };
};
// Key를 제네릭으로 받아 EventMap[Key]를 Payload 타입으로 자동 추론합니다.
function emit(eventName: K, payload: EventMap[K]) {
console.log(`Event: ${eventName}`, payload);
}
emit의 첫번째 값에 따라 두번째 인자의 타입이 다르게 결정됩니다. 해당 타입들은 위의 EventMap 타입에 정의되어 있습니다. 이제 다음처럼 emit 함수를 이용할 수 있습니다.
emit('click', { x: 10, y: 20 }); // ✅ 정상 작동
emit('click', { elementId: 'btn' }); // ❌ 타입 에러! click은 x, y가 필수입니다.
emit('hover', { elementId: 'header' }); // ✅ 정상 작동 (elementId 오타 시 감지)
그러나 emit 함수 구현부에서 첫번째 인자인 eventName의 값에 따라 payload의 타입을 결정해서 사용할 수 있어야 합니다. 예를들어 eventName이 ‘click’이라면 payload.x가 가능해야 합니다. 하지만 위의 방법으로는 안됩니다. 다른 방법은 다음과 같으며 TypeScript 코드로써 보다 더 쉽고 직관적입니다.
type EventMap =
| {
eventName: "click";
payload: { x: number; y: number };
}
| {
eventName: "hover";
payload: { elementId: string };
}
| {
eventName: "keyPress";
payload: { key: string; code: number };
};
function emit(event: EventMap) {
if (event.eventName === "click") {
event.payload.x; // ✅
event.payload.y;
}
if (event.eventName === "hover") {
event.payload.elementId; // ✅
}
}
emit({ eventName: 'click', payload: { x: 10, y: 20 } });
결국 함수 오버로딩을 스마트하게 대체하는 “매개변수 동적 추론” 방식은 크게 의미가 없습니다. 🙂
