[TypeScript] 함수 타입 정의하기

함수 타입 표현식

가장 기본적인 방법은 함수 타입 표현식입니다. 화살표 함수와 유사한 문법을 사용하여, 매개변수와 반환 타입을 명시할 수 있습니다.

const notify: (text: string) => void = (text) => {
  console.log(`알림: ${text}`);
};

notify("새 메시지가 도착했습니다."); // 출력: 알림: 새 메시지가 도착했습니다.

타입 별칭을 통한 함수 타입 정의

type FormatText = (input: string) => string;

const uppercase: FormatText = (input) => input.toUpperCase();
const trim: FormatText = (input) => input.trim();

console.log(uppercase("hello")); // 출력: HELLO
console.log(trim("  world  "));  // 출력: world

호출 시그니처

함수는 단순히 호출 가능한 값일 뿐만 아니라, 프로퍼티도 가질 수 있습니다. 호출 시그니처를 포함하는 객체 타입으로 정의하면, 함수로서의 기능과 추가 정보를 모두 기술할 수 있습니다.

interface MathFunction {
  label: string;
  (x: number, y: number): number;
}

const add: MathFunction = ((x, y) => x + y) as MathFunction;
add.label = "덧셈 함수";

console.log(add.label);       // 출력: 덧셈 함수
console.log(add(5, 3));       // 출력: 8

생성자 시그니처

`new`로 호출 가능한 함수 타입을 정의할 때는 생성자 시그니처를 사용합니다.

interface DeviceConstructor {
  new (model: string): Device;
}

class Device {
  constructor(public model: string) {}

  info(): string {
    return `모델: ${this.model}`;
  }
}

function buildDevice(ctor: DeviceConstructor, model: string): Device {
  return new ctor(model);
}

const gadget = buildDevice(Device, "SmartWatch");
console.log(gadget.info()); // 출력: 모델: SmartWatch

제네릭 함수

제네릭은 함수가 다양한 타입을 처리하며 타입 관계를 유지하게 합니다.

function getLast<T>(items: T[]): T | undefined {
  return items[items.length - 1];
}

const lastNum = getLast<number>([10, 20, 30]);
const lastStr = getLast<string>(["a", "b", "c"]);

console.log(lastNum); // 출력: 30
console.log(lastStr); // 출력: c

제네릭 타입 제한

`extends`로 제네릭 타입에 제약을 추가해 특정 속성을 강제할 수 있습니다.

function getKey<T extends { id: string }>(obj: T): string {
  return obj.id;
}

const item = { id: "abc123", name: "Item" };
console.log(getKey(item)); // 출력: abc123
// getKey({ value: 10 }); // 오류: id 속성 없음

함수 오버로드

오버로드 시그니처로 함수가 여러 형태로 호출될 수 있도록 정의합니다.

function parseInput(value: string): number;
function parseInput(value: number): string;
function parseInput(value: any): any {
  return typeof value === "string" ? parseInt(value) : value.toString();
}

console.log(parseInput("123")); // 출력: 123 (number)
console.log(parseInput(456));   // 출력: "456" (string)

선택적 매개변수와 나머지 매개변수

선택적 매개변수(`?`)와 나머지 매개변수(`...`)로 다양한 인자를 처리합니다.

// 선택적 매개변수 예제
function greet(name?: string): void {
  console.log(`Hello, ${name || "stranger"}!`);
}

greet();            // 출력: Hello, stranger!
greet("Alice");     // 출력: Hello, Alice!

// 나머지 매개변수 예제
function multiply(factor: number, ...values: number[]): number[] {
  return values.map((value) => value * factor);
}

console.log(multiply(3, 1, 2, 3));  // 출력: [3, 6, 9]

매개변수 구조 분해

객체 매개변수를 구조 분해해 간결하게 정의할 수 있습니다.

interface Config {
  host: string;
  port: number;
}

const connect: ({ host, port }: Config) => string = ({ host, port }) => {
  return `${host}:${port}`;
};

console.log(connect({ host: "localhost", port: 8080 })); // 출력: localhost:8080