함수에 타입 붙이기 — 매개변수·반환·옵션
함수 시그니처 기본·옵션 파라미터·기본값·rest·void·overload 까지 한 번에.
변수에 타입을 붙였다면, 다음은 함수입니다. TS 가 가장 빛나는 곳이 바로 여기 — "이 함수에 무엇을 넣으면 무엇이 나오나" 가 IDE 의 자동완성과 빨간 줄로 즉시 보입니다. 3편은 그 시그니처를 작성하는 모든 패턴을 정리합니다.
기본 시그니처 — 매개변수와 반환
// 함수 선언문
function add(a: number, b: number): number {
return a + b;
}
// 화살표 함수
const multiply = (a: number, b: number): number => a * b;
// 반환 타입 생략 (추론에 맡김 — 권장)
const greet = (name: string) => `안녕, ${name}!`;
// ^? const greet: (name: string) => string
매개변수는 항상 타입을 적어야 합니다(strict 모드 기준). 반환 타입은 추론에 맡겨도 됩니다 — 함수 안의 return 만 보면 TS 가 알아냅니다. 다만 외부에 공개하는 함수(라이브러리·API) 는 반환도 명시하는 게 좋습니다 — 본문이 바뀌어도 시그니처가 의도와 같은지 보장됩니다.
옵션 파라미터 · 기본값 · rest
// 옵션 파라미터 — ? 붙이면 안 줘도 OK
function makeUrl(path: string, query?: string): string {
return query ? `${path}?${query}` : path;
}
makeUrl("/users"); // OK
makeUrl("/users", "page=1"); // OK
// query 의 실제 타입은 string | undefined
// 기본값 — 안 주면 자동으로 사용
function fetch(url: string, retries = 3) {
// ^? number ← 추론
}
fetch("/api"); // retries=3
fetch("/api", 5); // retries=5
// rest 파라미터 — 가변 인자
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10
옵션 vs 기본값. query?: string 는 안 주면 undefined 가 됩니다. retries = 3 은 안 주면 3 이 됩니다. 둘은 다릅니다. "안 주면 어떤 동작" 이 명확할 땐 기본값 쪽이 코드가 깔끔해요.
void vs undefined — 헷갈리는 둘
// void — "이 함수는 의미 있는 반환이 없다"
function log(msg: string): void {
console.log(msg);
// return 없어도 OK
}
// undefined — "값으로 undefined 를 명시적으로 돌려준다"
function find(name: string): User | undefined {
return users.find(u => u.name === name);
// 못 찾으면 undefined 명시
}
한 줄 차이: void 는 "결과를 무시해라", undefined 는 "결과로 undefined 가 가능하다". void 함수를 변수에 받으면 그 값은 사용하지 않는 게 약속이고, undefined 반환은 사용 전 검사가 필요합니다.
함수 타입을 변수에 — 콜백·이벤트 핸들러
// 함수 타입 별칭
type BinaryOp = (a: number, b: number) => number;
const add: BinaryOp = (a, b) => a + b; // 매개변수 타입 추론됨
const sub: BinaryOp = (a, b) => a - b;
// 콜백 인자에 타입
function map<T, U>(arr: T[], fn: (item: T) => U): U[] {
return arr.map(fn);
}
// fn 의 시그니처가 명확 → IDE 가 fn 본문 작성 시 자동완성
함수를 다른 함수의 인자로 넘길 때(콜백), 그 인자 함수의 타입을 적어두면 호출자 쪽에서 화살표 함수 본문을 쓸 때 자동완성이 됩니다. 이게 라이브러리 API 가 편하게 느껴지는 비결입니다.
overload — 같은 함수, 다른 시그니처
// 시그니처 여러 줄 + 본문 한 번
function parse(input: string): string[];
function parse(input: number): number;
function parse(input: string | number): string[] | number {
if (typeof input === "string") return input.split(",");
return input * 2;
}
const a = parse("a,b,c"); // string[]
const b = parse(10); // number
호출하는 쪽에서 입력 타입에 따라 반환 타입이 달라지게 만들고 싶을 때 overload 를 씁니다. 다만 너무 자주 쓰지는 마세요 — 보통 유니온 + 조건부 타입(12편) 이 더 깔끔합니다.
실전 우선순위. ① 매개변수 항상 표기 → ② 공개 함수는 반환도 표기 → ③ 옵션은 ?, 기본값 있으면 = value → ④ 가변 인자는 ...args: T[] → ⑤ overload 는 정말 필요할 때만. 이 다섯 줄이면 입문~중급의 90% 함수가 끝납니다.
4편 — interface vs type
객체에 타입 붙이는 두 방법, 어떻게 다르고 어느 쪽을 골라야 할까.
이전: 2편 기본 타입 · 현재: 3편 (입문) · 다음 → 4편 interface vs type · 진행: 3/20