[TYPESCRIPT] this 타입과 함수 오버로드 — 호출 맥락을 타입으로 표현하는 고급 패턴

TypeScript에서 함수와 메서드를 설계하다 보면 “이 함수가 어떤 객체에서 호출되느냐”, “인자에 따라 반환 타입이 달라져야 하는 상황”을 자주 만나게 됩니다.

이때 중요한 개념이 바로 this 타입함수 오버로드(Function Overload)입니다. 이 두 가지를 잘 활용하면 메서드 체이닝, 빌더 패턴, 다형적인 API를 타입 안정성 있게 설계할 수 있습니다.

 

this 타입이란?

TypeScript에서 this 타입은 “해당 메서드를 호출한 객체 자신의 타입”을 의미합니다.

class Counter {
  value = 0;

  increment() {
    this.value++;
    return this;
  }
}

여기서 this의 타입은 Counter입니다. 즉, 반환 타입이 자동으로 Counter로 추론됩니다.

 

this 타입의 가장 큰 장점 — 메서드 체이닝

this 타입을 사용하면 상속 구조에서도 타입이 깨지지 않는 체이닝이 가능합니다.

class Base {
  log() {
    console.log("base");
    return this;
  }
}

class Child extends Base {
  childMethod() {
    return this;
  }
}

const c = new Child();
c.log().childMethod(); // 정상

만약 반환 타입을 Base로 명시했다면 childMethod() 호출은 불가능했을 것입니다.

 

함수에서 this 타입 명시하기

함수에서도 첫 번째 가짜 인자로 this 타입을 명시할 수 있습니다.

type User = {
  name: string;
};

function greet(this: User) {
  return `Hello ${this.name}`;
}

이 함수는 반드시 User 객체를 this로 바인딩해서 호출해야 합니다.

const user = { name: "Alice" };
greet.call(user); // OK
greet(); // ❌ 오류

콜백, 이벤트 핸들러, call/apply/bind 사용 시 매우 유용한 패턴입니다.

 

this 타입과 noImplicitThis

noImplicitThis 옵션이 켜져 있으면 this 타입이 명확하지 않은 경우 컴파일 에러가 발생합니다.

function foo() {
  console.log(this); // ❌ this: any
}

이 문제를 해결하는 가장 좋은 방법이 바로 명시적인 this 타입 선언입니다.

 

함수 오버로드란?

함수 오버로드는 하나의 함수에 여러 호출 시그니처를 정의하는 방식입니다.

function format(value: string): string;
function format(value: number): string;
function format(value: string | number) {
  return value.toString();
}

외부에서는 서로 다른 함수처럼 보이지만, 구현은 하나로 유지됩니다.

 

오버로드가 필요한 이유

유니온 타입만으로는 다음과 같은 문제가 발생합니다.

function bad(value: string | number) {
  return value.toFixed(2); // ❌ string에는 없음
}

오버로드를 사용하면 입력 타입에 따라 반환 타입을 정확히 표현할 수 있습니다.

 

반환 타입이 달라지는 오버로드

function parse(value: string): number;
function parse(value: number): string;
function parse(value: string | number) {
  return typeof value === "string"
    ? Number(value)
    : value.toString();
}

const a = parse("10"); // number
const b = parse(10);   // string

이 패턴은 API 파서, 유틸리티 함수에서 매우 자주 사용됩니다.

 

this 타입 + 함수 오버로드 조합

두 개념을 함께 사용하면 더욱 강력한 API를 만들 수 있습니다.

class Builder {
  value = "";

  set(value: string): this;
  set(value: number): this;
  set(value: string | number) {
    this.value = String(value);
    return this;
  }
}

new Builder()
  .set("hello")
  .set(123);

입력 타입에 따른 오버로드, this 반환으로 체이닝 유지 → 실무에서 가장 이상적인 빌더 패턴

 

실무에서 자주 쓰이는 패턴

  • Fluent API / Builder 패턴
  • DOM / 이벤트 핸들러 유틸리티
  • 설정 객체 체이닝
  • 다형적 유틸 함수
  • 라이브러리 Public API 설계

특히 외부에 노출되는 API에서는 this 타입 + 오버로드 조합이 사실상 표준입니다.

 

주의사항

  • 오버로드 시그니처와 구현 시그니처 불일치 주의
  • 구현 시그니처는 가장 넓은 타입으로 작성
  • 오버로드가 과도하면 가독성 저하
  • 단순 분기는 제네릭이나 유니온이 더 나을 수도 있음

 


 

 

this 타입함수 오버로드는 TypeScript에서 호출 맥락과 다형성을 안전하게 표현하는 중요한 도구입니다.

  • this 타입 → 호출 객체의 타입 유지, 체이닝 안전성
  • 함수 오버로드 → 입력/출력 타입 정확한 표현
  • 두 개를 조합하면 강력한 Public API 설계 가능