함수 타입 정의
함수의 매개변수와 반환값에 타입을 지정하고 선택·기본·나머지 매개변수와 호출 시그니처를 안전하게 선언합니다.
프로그래밍에서 함수(Function)는 특정 작업을 수행하는 코드 블록이자 프로그램의 핵심 구성 요소입니다.
자바스크립트 함수는 일급 객체(First-class Citizen)이므로 변수에 담거나 다른 함수의 인자로 넘기는 등 유연하게 사용할 수 있습니다.
타입스크립트는 이 유연성을 유지하면서도 함수를 더 안전하고 예측 가능하게 만드는 강력한 함수 타입 정의 기능을 제공합니다.
첫 프로그램의 검사·실행과 unknown·void의 경계를 바탕으로 함수의 입력·출력 계약을 읽습니다. 매개변수는 선언의 자리 이름이고, 인수는 호출할 때 전달하는 실제 값입니다. 아래 코드는 각기 별도 예제이므로 같은 이름의 함수를 한 파일에 중복 선언하지 않습니다.
TYPE / RUNTIME / CONTRACT
호출 타입을 검사하고 실제 인수로 실행합니다
add의 숫자 계약은 호출 전에 검사됩니다. 선택·기본·나머지 매개변수는 각자 다른 방식으로 본문에 값을 전달합니다.
CALL
숫자 두 개와 숫자 결과
add(5, 10)은 15입니다. 숫자 자리에 문자열을 넣거나 숫자 반환 함수에서 문자열을 반환하면 타입 오류입니다.
OPTIONAL / DEFAULT
누락과 기본값을 구분
lastName?은 생략될 수 있습니다. buildName의 truthiness 검사는 빈 문자열도 제외합니다. greet의 기본값은 undefined일 때 적용됩니다.
REST
남은 인수를 배열로 수집
sumAll(...numbers: number[])은 1·2·3을 받아 6을 반환합니다. 이 예제의 나머지 인수는 모두 숫자여야 합니다.
FUNCTION TYPE
함수 값도 같은 계약으로 검사
AddFunction에 대입한 myAdd와 myMultiply는 숫자 매개변수 두 개와 숫자 반환을 지킵니다. 함수 타입의 화살표는 구현 문법과 별개입니다.
정적 검사 과정과 런타임 값 처리는 구분됩니다. contextual void에서 반환값을 무시하는 규칙은 숫자 반환 계약과 다른 규칙입니다.
함수의 기본 타입 정의
가장 기본적인 함수 타입 정의는 함수의 매개변수(Parameters)와 반환값(Return Type)에 타입을 명시하는 것입니다.
이는 함수가 어떤 종류의 입력을 받아 어떤 종류의 출력을 내보낼지 명확히 선언하는 것과 같습니다.
// 1. 매개변수와 반환 값에 타입 명시
function add(x: number, y: number): number {
return x + y;
}
// 2. 반환 값이 없는 함수 (void)
function logMessage(message: string): void {
console.log(message);
}
// 3. 함수 표현식에 타입 정의
const subtract = function(a: number, b: number): number {
return a - b;
};
// 4. 화살표 함수에 타입 정의
const multiply = (a: number, b: number): number => {
return a * b;
};
console.log(add(10, 5)); // 15
logMessage("TypeScript 함수 타입 학습"); // TypeScript 함수 타입 학습
console.log(subtract(20, 7)); // 13
console.log(multiply(3, 4)); // 12
// 오류 예시: 잘못된 타입의 인자 전달
// add("hello", 5); // Error: 'string' 형식의 인수는 'number' 형식의 매개 변수에 할당될 수 없습니다.
// 오류 예시: 잘못된 반환 타입
// function invalidReturn(): number {
// return "이것은 숫자가 아닙니다."; // Error: 'string' 형식은 'number' 형식에 할당될 수 없습니다.
// }위 예시에서 핵심은 다음과 같습니다.
- 매개변수 타입: 매개변수 이름 뒤에 콜론(
:)을 붙이고 해당 매개변수가 받을 값의 타입을 명시합니다. (x: number) - 반환 타입: 매개변수 목록 괄호 뒤에 콜론(
:)을 붙이고 함수가 반환할 값의 타입을 명시합니다. (): number) - 반환값이 없는 함수는
void타입을 반환 타입으로 사용합니다.
선택적 매개변수
자바스크립트에서는 함수를 호출할 때 매개변수를 생략할 수 있습니다.
타입스크립트에서도 특정 매개변수가 필수적이지 않고 선택적으로 전달될 수 있도록 할 수 있습니다.
매개변수 이름 뒤에 물음표(?)를 붙여 선택적 매개변수로 만듭니다.
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return firstName + " " + lastName;
} else {
return firstName;
}
}
let result1 = buildName("김"); // 김
let result2 = buildName("이", "철수"); // 이 철수
// let result3 = buildName("박", "영희", "씨"); // Error: 예상되는 인수는 1-2개이지만 3개를 가져왔습니다.
console.log(result1);
console.log(result2);주의할 점: 선택적 매개변수는 항상 필수 매개변수 뒤에 위치해야 합니다.
즉, (arg1?: type, arg2: type)과 같은 형태는 허용되지 않습니다.
기본 매개변수 개념
ES6부터 자바스크립트에서는 함수 매개변수에 기본값(Default Value)을 설정할 수 있게 되었습니다.
타입스크립트에서도 이 기능을 지원합니다. 뒤에 필수 매개변수가 없다면 해당 인수를 생략할 수 있고, 뒤에 필수 인수가 있다면 기본값 위치에 undefined를 전달할 수 있습니다.
function greet(name: string = "게스트"): string {
return `안녕하세요, ${name}님!`;
}
console.log(greet("김아영")); // 안녕하세요, 김아영님!
console.log(greet()); // 안녕하세요, 게스트님! (매개변수를 생략하면 기본값이 사용됨)
// 기본 매개변수는 선택적 매개변수와 유사하게 동작합니다.
// 그러나 기본 매개변수 뒤에 필수 매개변수를 배치할 수도 있습니다.
function sendEmail(to: string, subject: string = "제목 없음", body: string): void {
console.log(`To: ${to}, Subject: ${subject}, Body: ${body}`);
}
// sendEmail("[email protected]", "안녕하세요"); // Error: 'body' 매개 변수에 대한 인수가 없습니다.
sendEmail("[email protected]", undefined, "반갑습니다."); // body는 필수이므로 정의해야 합니다.
sendEmail("[email protected]", "안부 인사", "잘 지내시죠?");기본 매개변수는 함수 호출 시 해당 인자를 제공하지 않으면 지정된 기본값을 사용하므로, 함수의 유연성을 높이면서도 예상치 못한 undefined 값을 다루는 경우를 줄여줍니다.
나머지 매개변수
함수가 정해지지 않은 수의 인자를 받을 때, 자바스크립트의 나머지 매개변수(Rest Parameters) 문법을 사용할 수 있습니다.
타입스크립트에서는 나머지 매개변수에 배열 타입을 명시하여, 해당 인자들이 모두 동일한 타입임을 보장할 수 있습니다.
function sumAll(...numbers: number[]): number {
let total = 0;
for (let i = 0; i < numbers.length; i++) {
total += numbers[i];
}
return total;
}
console.log(sumAll(1, 2, 3)); // 6
console.log(sumAll(10, 20, 30, 40)); // 100
// 오류 예시: 배열의 요소 타입이 맞지 않습니다.
// console.log(sumAll(1, 2, "3")); // Error: 'string' 형식의 인수는 'number' 형식의 매개 변수에 할당될 수 없습니다.나머지 매개변수는 함수 선언의 가장 마지막 매개변수이며, 이 예제에서는 숫자 배열 타입을 사용합니다. 튜플로 인수별 타입·개수를 표현하는 방식도 있으므로, 모든 rest 계약이 동일한 요소 타입을 요구하는 것은 아닙니다.
같은 기호 ...라도 선언의 rest는 여러 인수를 배열로 수집하고, 호출의 spread는 배열 같은 값의 요소를 인수로 펼칩니다. sumAll(...[1, 2, 3])을 호출하면 먼저 세 숫자를 펼친 뒤 함수의 numbers 배열로 모아 합계 6을 계산합니다.
함수 타입 별칭
복잡하거나 반복적으로 사용되는 함수 타입이 있다면, 타입 별칭(Type Aliases)을 사용하여 함수 자체의 타입을 정의할 수 있습니다.
이는 코드의 가독성을 높이고, 동일한 형태의 함수를 여러 곳에서 사용할 때 유용합니다.
// AddFunction이라는 함수 타입 별칭을 정의합니다.
// 이 타입은 두 개의 number 타입 매개변수를 받고 number 타입을 반환하는 함수를 의미합니다.
type AddFunction = (a: number, b: number) => number;
let myAdd: AddFunction = function(x, y) {
return x + y;
};
let myMultiply: AddFunction = (x, y) => x * y;
console.log(myAdd(5, 7)); // 12
console.log(myMultiply(5, 7)); // 35
// 오류 예시: 정의된 함수 타입과 반환 타입이 다릅니다.
// let myInvalidAdd: AddFunction = (x, y) => "결과: " + (x + y);
// Error: 'string' 형식은 'number' 형식에 할당될 수 없습니다.
// 매개변수 타입은 추론되므로 생략 가능합니다.
// let anotherAdd: AddFunction = (x, y) => x + y; // x와 y는 number로 추론됩니다.type AddFunction = (a: number, b: number) => number;와 같이 정의하며, 이는 마치 함수가 변수처럼 취급되어 특정 형태를 따르도록 강제하는 것과 같습니다.
함수 타입 정의는 타입스크립트의 핵심적인 부분입니다.
함수의 매개변수와 반환값에 명확한 타입을 부여함으로써, 개발 과정에서 발생할 수 있는 많은 오류를 사전에 방지하고 코드의 신뢰성을 높일 수 있습니다.
함수 타입 별칭의 이름은 동작을 생성하지 않습니다. 같은 AddFunction에 더하기와 곱하기를 모두 넣을 수 있으므로, 타입과 별도로 실제 출력이 요구한 계산인지 확인합니다.
예시
작은 상품 수량 사례에서 숫자 타입과 유효한 수량의 조건을 구별합니다. 단가는 1200으로 두고 수량은 0 이상의 정수만 허용합니다.
type PricePolicy = (unitPrice: number, quantity: number) => number;
const subtotal: PricePolicy = (unitPrice, quantity) => {
if (!Number.isInteger(quantity) || quantity < 0) {
throw new RangeError('수량은 0 이상의 정수여야 합니다.');
}
return unitPrice * quantity;
};
console.log(subtotal(1200, 3)); // 3600
console.log(subtotal(1200, 0)); // 0
// subtotal(1200, 1.5); // 타입은 통과하지만 실행 시 RangeError
// subtotal(1200, "3"); // 정적 타입 오류PricePolicy가 두 숫자를 받아 숫자를 반환한다는 형태를 검사합니다. 1.5도 number이므로 타입 표기만으로 정수 조건을 판단하지 못하며, 본문의 검사에서 거절합니다. logMessage처럼 void를 명시하는 경우와 호출자가 콜백 반환값을 무시하는 경우는 앞 절의 구분을 유지합니다.
혼자 확인하기
수량 4, -1, 문자열 "4"를 각각 전달하면 합계·실행 오류·정적 오류 중 무엇을 먼저 확인해야 할까요? sumAll()은 인수를 하나도 주지 않을 때 무엇을 반환하는지도 원 반복문을 따라 계산합니다.
계약과 값 조건 해설
수량 4는 1200×4=4800입니다. -1은 number 계약을 통과하지만 런타임 수량 검사에서 RangeError가 납니다. 문자열 "4"는 number 매개변수에 맞지 않아 정적 오류이며, Number("4") 같은 변환을 하더라도 결과를 업무 조건에 맞게 검사하는 책임은 남습니다. sumAll()의 numbers는 빈 배열이고 반복문이 실행되지 않아 초기 합계 0을 반환합니다.