프로그래밍TypeScript 개발자

TypeScript에서 함수 오버로드를 시그니처 수준에서 구현하고 올바르게 타입 지정하는 방법은 무엇인가요? 여러 함수 본체를 사용할 수 없는 이유는 무엇이며, 함수 본체 내 매개변수의 타입은 어떻게 처리됩니까? 실제 사용 시나리오와 가장 흔한 오류를 제시하십시오.

Hintsage AI 어시스턴트로 면접 통과

답변.

함수 오버로드는 다양한 유형의 매개변수와 반환 값을 가진 단일 함수를 생성할 수 있도록 합니다. C#이나 Java와 같은 다른 언어에서는 동일한 이름을 가진 여러 함수를 정의할 수 있지만, 각기 다른 유형이나 인수의 수를 가질 수 있습니다. TypeScript는 타입 수준에서 시그니처 오버로드를 통해 이 메커니즘을 지원하지만, 함수 본체는 항상 하나여야 합니다.

문제는 JavaScript가 타입이나 매개변수 수에 대한 오버로드를 기본적으로 지원하지 않기 때문에 발생합니다. TypeScript의 등장으로 여러 함수 시그니처를 연속으로 선언하고 그에 대한 하나의 구현을 수동으로 분별하여 작동시키게 됩니다.

문제가 발생하는 이유는 선언한 시그니처와 일치하지 않으면, TypeScript는 함수 본체 내의 매개변수에 가장 일반적인 타입(모든 매개변수 타입의 결합)을 할당하게 되고, 프로그래머는 검사와 타입 가드를 수행해야 하기 때문입니다.

해결책: 명시적 시그니처에서 오버로드를 엄격하게 타입 지정하고, union types와 type guards를 적절히 활용하며, 동작을 정확하게 문서화합니다.

코드 예시:

function format(value: string): string; function format(value: number, locale: string): string; function format(value: any, locale?: string): string { if (typeof value === 'number') { return value.toLocaleString(locale); } return value.trim(); } format(' hello '); // 'hello' format(123456, 'ru-RU'); // '123 456'

주요 특징:

  • 여러 오버로드 시그니처가 순차적으로 선언되고, 그 아래에 하나의 구현이 존재함.
  • 함수 본체 내에서는 union 타입 또는 any/unknown을 사용하여 매개변수를 처리함.
  • 함수 본체의 타입은 시그니처보다 넓고 일반적임.

신중한 질문들.

C#처럼 동일한 이름을 가진 여러 함수를 다른 본체로 선언할 수 있나요?

아니요. TypeScript(및 JavaScript)에서는 사실상 그 이름을 가진 단일 함수만 존재합니다. 오버로드는 컴파일러를 위한 타입 수준에서만 작동하며, 실제로는 함수 본체가 하나입니다.

가장 일반적인 매개변수를 가진 시그니처를 오버로드 시그니처 후에 구현하지 않으면 어떻게 되나요?

TypeScript는 컴파일 오류를 발생시킵니다. 항상 모든 가능한 오버로드 옵션을 포괄하는 구현 함수가 하나 존재해야 합니다.

function foo(a: string): string; function foo(a: number): number; // 본체 없음 — 오류

함수 본체 내에서 특정 시그니처에 특화된 속성을 사용할 수 있나요?

아니요, 타입 검사(type guard) 후에만 가능합니다. 그렇지 않으면 TypeScript는 현재 호출되고 있는 시그니처에 따라 이러한 속성을 직접 사용하는 것을 허용하지 않습니다.

function bar(x: string): number; function bar(x: number): number; function bar(x: string | number): number { if (typeof x === 'string') return x.length; return x * 2; }

일반적인 오류 및 안티 패턴

  • 오버로드 시그니처 후 구현을 추가하지 않음 — 컴파일이 실패함.
  • 함수 본체 내에서 타입 검사 없이 특정 속성을 사용함.
  • 오버로드를 지나치게 복잡하게 만들어 가독성이 떨어짐.
  • 명확하게 반환 타입을 정의하지 않거나 함수 로직 변경 시 시그니처를 업데이트하는 것을 잊음.

실제 사례

부정적 사례

보편적인 시그니처-구현을 추가하지 않음:

function sum(a: string, b: string): string; function sum(a: number, b: number): number; // 구현 없음 — 컴파일러 오류

장점:

  • 편리한 API를 설명하고자 하는 아이디어

단점:

  • 코드가 컴파일되지 않아 어떤 경우도 처리할 수 없음

긍정적 사례

표준에 따라 오버로드를 구현함:

function sum(a: string, b: string): string; function sum(a: number, b: number): number; function sum(a: any, b: any): any { return typeof a === 'string' && typeof b === 'string' ? a + b : a + b; }

장점:

  • 모든 경우가 올바르게 처리되고 컴파일 단계에서 타입이 결정됨

단점:

  • 함수 본체가 타입을 수동으로 제어해야 하며, 각 경우를 검사하는 것을 잊지 말아야 함.