어제 오늘 내일

[TypeScript] 8. 함수와 타입 — 오버로드, 선택적 매개변수, this 본문

IT/TypeScript

[TypeScript] 8. 함수와 타입 — 오버로드, 선택적 매개변수, this

hi.anna 2026. 8. 10. 18:45

이 글의 목표: 함수를 타입 관점에서 완전히 다룹니다. 매개변수 옵션, 기본값, 나머지 매개변수, 함수 타입, 오버로드까지.

매개변수와 반환 타입

// (매개변수: 타입) => 반환타입
function add(a: number, b: number): number {
  return a + b;
}

// 화살표 함수도 동일
const multiply = (a: number, b: number): number => a * b;

반환값이 없으면 void입니다.

function log(message: string): void {
  console.log(message);
  // return 문이 없음
}

선택적 매개변수(?)

// greeting은 없어도 된다
function greet(name: string, greeting?: string): string {
  // greeting은 string | undefined 이므로, 없을 때를 처리해야 한다
  return `${greeting ?? "안녕"}, ${name}`;
}

greet("철수");         // "안녕, 철수"
greet("철수", "반가워"); // "반가워, 철수"

??(널 병합 연산자)는 왼쪽이 null/undefined일 때만 오른쪽을 씁니다. 선택적 매개변수의 기본값 처리에 잘 어울립니다.

주의: 선택적 매개변수는 반드시 필수 매개변수 뒤에 와야 합니다. function f(a?: number, b: number)는 에러입니다.

기본값 매개변수

기본값을 주면 ? 없이도 생략 가능해지고, 타입도 자동 추론됩니다.

// greeting의 타입은 기본값 "안녕"을 보고 string으로 추론된다
function greet(name: string, greeting = "안녕"): string {
  return `${greeting}, ${name}`;
}

greet("영희"); // "안녕, 영희"

나머지 매개변수(rest)

개수가 정해지지 않은 인자를 배열로 받습니다.

// ...numbers는 number[] 타입
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

sum(1, 2, 3);       // 6
sum(1, 2, 3, 4, 5); // 15

함수 타입 표현

함수 자체를 타입으로 정의할 수 있습니다. 콜백을 받을 때 특히 유용합니다.

// number 하나를 받아 boolean을 반환하는 함수 타입에 이름을 붙인다
type Predicate = (value: number) => boolean;

function filterNumbers(arr: number[], test: Predicate): number[] {
  return arr.filter(test);
}

const evens = filterNumbers([1, 2, 3, 4], (n) => n % 2 === 0); // [2, 4]
// (n) => ... 에서 n의 타입은 Predicate로부터 number로 자동 추론된다

호출 시그니처와 객체

함수이면서 속성도 가진 객체는 이렇게 표현합니다.

// 호출 가능하면서 description 속성도 갖는 함수 타입
type DescribedFunction = {
  description: string;
  (arg: number): boolean; // 호출 시그니처
};

const isEven: DescribedFunction = (n) => n % 2 === 0;
isEven.description = "짝수 판별";

함수 오버로드

같은 함수가 인자 형태에 따라 다른 반환 타입을 가질 때 씁니다.

// 오버로드 시그니처들 (구현부 없이 형태만 선언)
function parse(value: string): string[];
function parse(value: number): number[];

// 실제 구현 (시그니처들을 모두 만족해야 함)
function parse(value: string | number): string[] | number[] {
  if (typeof value === "string") {
    return value.split("");
  }
  return [value];
}

const a = parse("abc"); // 타입: string[]  (오버로드 덕분에 정확히 좁혀짐)
const b = parse(42);    // 타입: number[]

오버로드가 없다면 반환 타입이 string[] | number[]로 뭉뚱그려집니다. 오버로드는 "입력에 따라 출력 타입이 달라진다"를 정확히 표현합니다.

다만 오버로드는 복잡해지기 쉽습니다. 많은 경우 제네릭(10편)이나 유니온으로 더 깔끔하게 풀 수 있으니, 정말 필요할 때만 쓰세요.

this 타입

콜백에서 this가 무엇을 가리키는지 명시할 수 있습니다. 첫 번째 "가짜 매개변수"로 선언합니다.

type Button = {
  label: string;
  onClick(this: Button): void; // 이 메서드 안의 this는 Button 타입
};

const btn: Button = {
  label: "확인",
  onClick() {
    // this가 Button으로 타입 지정되어 label에 안전하게 접근
    console.log(`${this.label} 클릭됨`);
  },
};

this: Button은 실제 매개변수가 아니라 타입 정보일 뿐이며, 컴파일 시 사라집니다.

정리

  • 선택적(?)과 기본값 매개변수로 유연한 함수를 만든다.
  • 나머지 매개변수(...)로 가변 인자를 배열로 받는다.
  • 함수 타입((a: T) => R)으로 콜백을 안전하게 표현한다.
  • 오버로드는 입력에 따라 출력 타입이 달라질 때 쓰되, 남용하지 않는다.
  • this 타입으로 메서드 내 this를 명확히 한다.

다음 글에서는 TypeScript가 타입을 비교하는 근본 원리, "구조적 타이핑"을 다룹니다.

반응형
Comments