C.W.K.
Stream
Lesson 02 of 05 · published

함수 Overload: 한 이름, 여러 Signature

~11 min · functions, overloads, one-name-many-signatures

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Overload 는 한 함수가 사실 같은 모자 쓴 여러 함수일 때 쓰는 거."

Overload 의 모양

함수 overload 는 단일 함수에 여러 call signature 주는 방법. 먼저 overload signature 들 (공개 contract) 쓰고, 그다음 다 union 으로 받는 단일 구현 signature. 구현 signature 는 직접 호출 안 됨 — overload signature 만 호출 가능.

function format(value: number): string;
function format(value: Date): string;
function format(value: number | Date): string {
  // 구현이 둘 다 처리
  return value instanceof Date ? value.toISOString() : value.toFixed(2);
}

Caller 가 2개 구별된 signature 봐: `number` 나 `Date` 전달, `string` 받음. Compiler 가 호출을 구현으로 라우팅. 구현이 runtime 체크로 두 입력 처리.

Overload 가 옳은 tool 일 때

함수가 진짜로 여러, 구별된 call 모양 가지고 AND 단일 signature (union 타입, generic, conditional 타입) 가 그 관계 깔끔히 표현 못 할 때 overload 써.

반례 — 단일 signature 이기는 자리들:

  • 같은 parameter, 여러 타입: function format(value: number | Date): string — union, overload 불필요.
  • 타입 의존 return: function wrap<T>(x: T): { value: T } — generic, overload 불필요.
  • 입력 기반 조건부 return: function f<T>(x: T): T extends string ? number : string — conditional 타입, overload 불필요.

Overload 가 빛나는 곳: Call 모양들이 다른 parameter 개수 가질 때, 또는 구별된 call 모양들이 IDE hover 에 구별된 문서 필요할 때.

Overload-vs-union 규칙: 함수의 parameter 와 return 타입이 단일 signature 통해 연결돼 있으면, 그 signature 써 (union, generic, conditional 타입 함께). 연결 안 돼 있으면 — 예: "호출 형태 A 또는 호출 형태 B, 공유 타입 없음" — overload 써.

구현-signature 함정

구현 signature 는 private — caller 가 볼 수 없음. 근데 모든 overload signature 의 union 을 받아야 함. Overload 4개 쓰고 narrow 한 구현 쓰면 compiler 불평.

더 나빠: 구현 signature 는 호환성 체크되지만 public 타입의 일부 아님. any 받아도 caller 는 public signature 만 사용 가능. 이게 "내 구현이 any 받으니까 이 호출 작동해" 가정하는 developer 잡아.

피파의 고백

cwkPippa 전체 frontend 에 overload 된 함수 한 3개. 그중 2개는 generic 으로 됐을 거. 1개가 진짜 overload 필요 — with-id-only vs with-id-and-filter 두 다른 호출 모양이 다른 결과 모양 반환하는 API caller. 교훈: overload 는 실제 tool, 근데 대부분 시도가 generic 원하는 걸로 밝혀져. Generic 먼저 시도.

Code

Overload — return 이 어느 형태 호출하느냐에 따라 다를 때·typescript
// 진짜 overload — 다른 모양, 다른 return 타입.

function findUser(id: number): User;
function findUser(query: { email: string }): User[];
function findUser(arg: number | { email: string }): User | User[] {
  if (typeof arg === 'number') {
    return getUserById(arg);             // 단일 User 반환
  } else {
    return searchUsersByEmail(arg.email); // User[] 반환
  }
}

const u: User = findUser(1);             // ✅ 첫 번째 overload 선택
const us: User[] = findUser({ email: 'a@b.c' });  // ✅ 두 번째 선택

// 구현 signature 는 직접 호출 안 됨.
// findUser('hello');                    // ❌ matching overload 없음
대부분 'overload-shaped' 문제가 generic 이나 conditional 타입 원함·typescript
// 반례: union + generic 보통 충분.

// Overload 가치 없음 — 그냥 param 을 union.
function format(v: number | Date): string {
  return v instanceof Date ? v.toISOString() : v.toFixed(2);
}

// Overload 가치 없음 — generic + conditional.
type WrapReturn<T> = T extends string ? { kind: 'str'; value: T } : { kind: 'num'; value: T };
function wrap<T extends string | number>(x: T): WrapReturn<T> {
  return (typeof x === 'string' ? { kind: 'str', value: x } : { kind: 'num', value: x }) as WrapReturn<T>;
}

const a = wrap('hi');   // a: { kind: 'str'; value: 'hi' }
const b = wrap(42);     // b: { kind: 'num'; value: 42 }

External links

Exercise

Overload 된 parse 함수 써: parse(input: string): string[] (comma 로 split) 와 parse(input: string, json: true): unknown (JSON.parse). 구현이 둘 다 처리. 그다음 generic 과 conditional return 타입 가진 단일 함수로 다시 써. 어느 버전이 더 깨끗하게 읽혀? 어느 게 확장 더 쉬워?
Hint
Overload 버전이 더 명시적이고 문서처럼 읽혀. Generic 버전이 더 compositional 하고 새 return 모양 추가하면서 더 잘 scale. 옳은 답은 프로젝트에 달림 — 근데 대부분 팀이 default 하나로 정하고 default 안 맞을 때만 다른 거 써.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.