Skip to main content

Command Palette

Search for a command to run...

타입스크립트의 모든 다형성

Published
타입스크립트의 모든 다형성

원문: Adrien Gautier, "Every polymorphism in TypeScript"

타입스크립트의 함수 오버로딩에 대해 글을 쓰다가 프로그래밍 이론에서 다형성세 가지 주요 유형을 우연히 알게 되었습니다. 이 글에서는 타입스크립트에서의 이 세 가지 유형의 다형성을 간단한 예제들로 소개하고자 합니다.

파라미터 다형성

타입스크립트에서 제네릭 함수를 사용한다면 이 패턴을 모른 채 사용했을 수도 있습니다. 별 기능이 없는 아래 함수를 살펴보세요.

function arrayEntries<T>(items: Array<T>): Array<[number, T]> {
  return items.map((item, index) => [index, item]);
}

함수 arrayEntries는 선언 중에 입력될 타입을 알 수 없으므로 "제네릭"이라고 불립니다. 함수가 호출되면 반환될 데이터 타입을 추론할 것입니다.

const entries = arrayEntries(['one', 'two']); // [[0, 'one'], [1, 'two']]

위 예제에서 변수 entries는 인자에 문자열이 들어오므로 Array<[number, string]> 타입을 받습니다.

"제네릭 함수"는 파라미터 다형성 함수의 다른 이름일 뿐입니다. 따라서 Array<T>와 같은 제네릭 타입도 파라미터 다형성의 한 형태라고 생각합니다만, 제 말을 그대로 받아들이지는 마세요.

서브타입 다형성

서브타입 다형성은 클래스에 의존하기 때문에 타입스크립트에서는 덜 일반적입니다.

interface Pet {
  speak: () => void;
}

class Dog implements Pet {
  speak() {
    return 'woof woof!';
  }
}

class Cat implements Pet {
  speak() {
    return 'meow!';
  }
}

위 예제에서 DogCatPet의 서브타입입니다. Pet의 모든 서브타입을 허용하는 함수 listen을 만들 수도 있습니다.

function listen(pet: Pet): void {
  console.log(pet.speak());
}

이를 서브타입 다형성이라고 합니다. speak 메서드는 Pet의 서브타입인 클래스 모두가 사용할 수 있고 클래스마다 고유한 동작을 수행할 수 있습니다.

애드혹 다형성

애드혹 다형성을 저는 "다형성 함수"라고 부릅니다. 더 정확히 말하면 애드혹은 Christopher Strachey가 함수 오버로딩이라고도 하는 이 개념을 분류하기 위해 사용한 용어입니다.

애드혹 다형성은 인자에 따라 동작이 달라진다는 점에서 파라미터 다형성 및 서브타입 다형성과 다릅니다. 즉, 인자의 타입에 따라 특정 구현이 호출됩니다.

C++에서의 함수 오버로딩

위키피디아에서 가져온 아래의 C++ 예제는 동일한 함수 Volume에 대해 서로 다른 구현 (따라서 서로 다른 동작)이 공존하는 것을 보여 줍니다.

int Volume(int s) {  // 정육면체의 부피.
  return s * s * s;
}

double Volume(double r, int h) {  // 원기둥의 부피.
  return 3.1415926 * r * r * static_cast<double>(h);
}

long Volume(long l, int b, int h) {  // 직육면체의 부피.
  return l * b * h;
}

int main() {
  std::cout << Volume(10);
  std::cout << Volume(2.5, 8);
  std::cout << Volume(100l, 75, 15);
}

타입스크립트에서의 함수 오버로딩

타입스크립트에서는 접근 방식이 조금 다릅니다. 동일한 예제를 사용하여 함수 Volume의 각 동작에 대해 (오버로드 시그니처라고도 하는) 하나의 선언을 작성하는 것으로 시작합니다. 저는 보통 각 선언에 JSDoc 주석을 포함시킵니다.

/**
 * 정육면체의 부피를 계산합니다.
 * @param {number} s - 정육면체의 변.
 * @returns {number} 정육면체의 부피.
 */
function Volume(s: number): number;

/**
 * 원기둥의 부피를 계산합니다.
 * @param {number} r - 원기둥 바닥의 반지름.
 * @param {number} h - 원기둥의 높이.
 * @returns {number} 원기둥의 부피.
 */
function Volume(r: number, h: number): number;

/**
 * 직육면체의 부피를 계산합니다.
 * @param {number} l - 직육면체의 세로.
 * @param {number} w - 직육면체의 가로.
 * @param {number} h - 직육면체의 높이.
 * @returns {number} 직육면체의 부피.
 */
function Volume(l: number, h: number, b: number): number;

그런 다음 함수의 실제 구현을 선언합니다.

function Volume(d: number, h?: number, b?: number): number {
  if (h !== undefined && b !== undefined) {
    return d * h * b;
  }
  if (h !== undefined) {
    return Math.PI * d * d * h;
  }
  return d * d * d;
}

C++와 다르게 하나의 구현이 함수 Volume다양한 동작을 책임집니다. 여기서 제공된 인자 수에 따라 if 문으로 함수의 동작을 조건부로 지정합니다.

console.log(Volume(10)); // 정육면체의 부피를 보여 줍니다.
console.log(Volume(2.5, 8)); // 원기둥의 부피를 보여 줍니다.
console.log(Volume(100, 75, 15)); // 직육면체의 부피를 보여 줍니다.

다형성이 아닌 것

위 예제에서 단일 구현여러 선언과 함께 사용되는 것을 보았습니다. 이는 각 동작에 고유한 구현이 있는 다른 언어와는 매우 다릅니다. 그렇다면 이를 정말 애드혹 다형성이라고 할 수 있을까요?

이 질문에 대한 명확한 답을 드릴 수는 없지만 타입스크립트에서 함수 오버로딩 방식을 사용할 때의 두 가지 장점을 강조하고 싶습니다.

이전 예제에서 함수 오버로딩은 함수 Volume사용법을 제한합니다. 아래와 같이 함수를 호출할 수 없습니다.

Volume(100, undefined, 15); // 이는 성립하지 않습니다.

이는 함수의 단일 구현만으로 가능할 수도 있지만 오버로드 시그니처가 이를 방지합니다. 또한 JSDoc 주석 덕분에 함수를 사용하는 모든 방법이 IDE에 명확하게 표시되어 있습니다.

IDE에서 제공되는 제안의 예시

함수 오버로딩이 타입스크립트에서 어떻게 구현되든 코드 품질을 개선하고 오류를 방지할 수 있다고 생각합니다.

요약

지금까지 타입스크립트에서 매개변수 다형성, 서브타입 다형성, 애드혹 다형성이 어떻게 구현되는지와 함수 오버로딩이 다른 프로그래밍 언어와 무엇이 다른지 살펴보았습니다. 다음 글에서는 타입스크립트에서 함수 오버로딩의 한계와 다형성 함수를 구현하기 위해 가능한 몇 가지 대안에 대해 자세히 살펴보겠습니다.

More from this blog

나의 오픈 소스 시작 이야기

원문: TkDoDo, “My Open Source Origin Story“ 가끔씩 제가 받는 질문이 하나 있는데, 바로 오픈 소스와 리액트 쿼리(React Query)를 어떻게 시작하게 되었는지입니다. 저의 기본 원칙은 어떤 질문을 세 번 받으면 더 이상 답변할 필요가 없도록 질문에 대해 글로 쓴다는 것입니다. 하지만 이 질문은 주로 직접 만났을 때 받는 질문이라 글로 작성할 생각을 한 적이 없었습니다. 최근에 오프라인 컨퍼런스에 더 많이 참...

Jul 30, 2025
나의 오픈 소스 시작 이야기

이더넷이란?

원문: baeldung, “What Is Ethernet?“ 1. 소개 이 튜토리얼에서는 이더넷(Ethernet)과 이를 통해 이루어지는 데이터 전송에 대해 알아보겠습니다. 2. 이더넷이란? 이더넷은 근거리 통신망(LAN) 또는 광역 네트워크(WAN) 내에서 장치들이 데이터를 주고받고 통신하기 쉽게 만들어 주는 널리 사용되는 기술입니다. 컴퓨터, 프린터, 서버는 물론 스마트 홈 기기까지도 이더넷으로 연결됩니다. 가정이나 사무실처럼 제한된 공간...

Jul 20, 2025
이더넷이란?

포스트 개발자 시대

원문: Josh W. Comeau, "The Post-Developer Era" 2년 전 2023년 3월, "프런트엔드 개발의 종말"이라는 제목의 블로그 글을 발행했습니다. 이는 OpenAI가 GPT-4 쇼케이스를 발표한 직후였고, 당시 업계 분위기는 머지않아 인간 소프트웨어 개발자는 필요 없어지고 앞으로는 소프트웨어 개발을 AI가 전담하게 될 것이라는 전망이 지배적이었습니다. 저는 이런 주장에 회의적이었고 그 블로그 글에서 소프트웨어 개발...

Jul 10, 2025
포스트 개발자 시대

널리 사용되는 네트워크 프로토콜

원문: Subham Datta, "Popular Network Protocols" 1. 개요 이 튜토리얼에서는 가장 널리 사용되고 인기 있는 네트워크 프로토콜들을 소개합니다. 2. 네트워크 프로토콜 소개 의사소통과 정보 교환은 현대 사회에서 가장 중요하고 강력한 역량입니다. 컴퓨터 네트워킹이란 여러 대의 컴퓨터와 장치를 케이블이나 위성을 통해 서로 연결하여, 거리와 상관없이 정보·자원·데이터베이스 등을 공유할 수 있게 하는 것을 말합니다. 네...

Jun 20, 2025
널리 사용되는 네트워크 프로토콜

커맨드 라인에 편해지는 법

원문: Julia Evans, "What helps people get comfortable on the command line?" 가끔 커맨드 라인을 써야 하는 친구들과 이야기하다 보면 많은 이들이 여전히 터미널을 두려워하고 있다는 걸 느낍니다. 그럴 때마다 어떤 조언을 할지 잘 모르겠더라고요. 저는 워낙 오래전부터 터미널을 써왔기 때문이죠. 그래서 Mastodon에 이렇게 물어봤습니다. 최근 1~3년 사이에 터미널 공포(?)를 극복한 분...

Jun 10, 2025
커맨드 라인에 편해지는 법
C

CodeSnap

84 posts

한국어로 전달하는 웹 개발 번역 매거진