함수와 타입
1. 함수에서 사용하기
타입스크립트에서는 함수가 어떤 타입의 값을 받을지, 어떤 타입을 리턴하는지 정해줄 수 있습니다.
function 함수명(파라미터: 타입): 리턴타입 {}
function 함수명(파라미터: 타입): 리턴타입 {}
간단한 덧셈 함수를 만들겠습니다.
function add(a, b) {
return a + b;
}
function add(a, b) {
return a + b;
}
이렇게 작성하면 add함수의 입력으로 받는 a, b, 그리고 리턴하는 값의 타입이 any로 설정됩니다. 우린 파라미터가 숫자였으면 좋겠습니다. 그리고 이 함수가 리턴하는 값 또한 숫자였으면 합니다. 사용하는 경우에도 문자열을 더해주는 것이 아님을 확실히 알면 좋겠네요.
function add(a: number, b: number): number {
return a + b;
}
function add(a: number, b: number): number {
return a + b;
}
이렇게 작성하면 함수에 어떤 값을 넣으면 좋은지 반환되는 값은 어떤 형태인지 바로 알 수 있습니다. 또한 명시하지 않아도 반환 가능한 모든 타입을 타입스크립트가 추론하여 알려주기도 합니다. 함수 뒤에 number를 지우고 함수 이름에 마우스를 올려보시면 number
라고 나오는 것을 확인할 수 있습니다.
이어서 아래 사항을 구분하여 함수를 살펴보겠습니다.
- 매개변수를 여러개 받을 수 있지만 입력하지 않아도 되는 경우
- 반환값이 없는 경우
- 함수가 끝나지 않는 경우
1.1 선택적 매개변수
만약 앞서 작성한 코드에 첫번째 매개변수만 들어오는 경우를 만들고 싶을 수 있습니다. 그럴 때는 선택적 매개변수를 사용할 수 있습니다.
function add(a: number, b?: number): number {
return a + b;
}
function add(a: number, b?: number): number {
return a + b;
}
?
를 매개변수 뒤에 붙이고 타입을 작성하면 해당 매개변수는 들어오지 않을 수 있다는 것을 의미합니다. 다만 이렇게 작성하게 되면 b 경고가 뜨게 됩니다. 자바스크립트에서는 함수에 매개변수가 제공되지 않으면 해당되는 매개변수는 undefined가 됩니다. 아무것도 들어오지 않을 경우 undefined가 될 것을 경고하고 있는 것입니다.
아래와 같이 작성하면 애러가 발생하지 않습니다. 이러한 방식을 타입좁히기라고 합니다. 조건문을 사용하여 b가 있을 때만 더해주는 코드를 작성해보겠습니다.
function add(a: number, b?: number): number {
if (b) {
return a + b;
}
return a;
}
function add(a: number, b?: number): number {
if (b) {
return a + b;
}
return a;
}
여기서 return a;
구문을 주석처리하면 add함수에 경고가 뜨게 됩니다. return이 undefined가 될 수 있기 때문입니다.
💡 typescript config에는 return에 undefined를 허용할지 안할지 결정하는 noImplicitReturns
라는 옵션이 있습니다. 해당 옵션을 false로 설정하면 타입스크립트의 타입추론을 통해 string|undefined
가 타입으로 할당됩니다. 물론 빨간 경고줄도 사라집니다.
추가로 아래와 같은 코드는 애러가 발생되지 않습니다. b가 undefined인 경우에도 "문자열" + undefined는 "문자열undefined"가 되어 문자열을 반환하기 때문입니다. 애러가 실제 발생될 수 있는 가능성이 있는 코드에서만 경고가 발생합니다. 처음 언급한 코드의 경우 number + undefined
는 NaN
이 되며, 이는 예기치 않은 결과를 초래할 수 있기 때문에 경고가 발생합니다.
function add(a: string, b?: number): string {
return a + b;
}
function add(a: string, b?: number): string {
return a + b;
}
1.2 반환값이 없는 경우
void는 함수의 return이 없는 경우에 사용하는 반환타입입니다.
function log(message: string): void {
console.log(message);
}
console.log(log('hello world'));
function log(message: string): void {
console.log(message);
}
console.log(log('hello world'));
해당 함수는 'hello world'와 함께 undefined를 반환합니다. 그러나 함수에서 void라 명시함으로써 반환값이 없음을 명시합니다.
function add(a: number, b: number): number|void {
if (a >= 0 || b >= 0) {
return a + b;
}
}
function add(a: number, b: number): number|void {
if (a >= 0 || b >= 0) {
return a + b;
}
}
위 코드는 리턴값이 number 또는 void가 될 수 있습니다. 만약 조건문이 거짓이라면 리턴값이 없기 때문에 void가 됩니다.
💡 void는 사용하기 위한 값이 아니며 반환되는 값도 아닙니다. 함수의 반환 형태를 나타내며 단순히 반환 값이 없음을 알리는데 사용됩니다.
1.3 never
never 반환타입은 함수가 절대로 끝나지 않는 경우나 return이 발생하지 않는 경우에 사용됩니다. 무한으로 반복되는 함수 또는 의도적으로 에러를 발생시키는 경우에는 함수가 끝나지 않았다고 볼 수 있습니다. 이런 경우를 명시적으로 나타내기 위해 사용됩니다.
타입을 명시하지 않으면 void로 타입이 추론됩니다 기본적으로 타입스크립트는 return이 없으면 void로 추론됩니다. 종료되지 않는 함수를 사용하는 입장에서는 내부 로직을 생각하지 않으면 함수가 종료되지 않는 경우를 생각하기 힘들 수도 있습니다. 이런 경우에는 함수가 종료되지 않는다고 알려주면 좋습니다.
function error(message: string): never {
throw new Error(message);
}
function error(message: string): never {
throw new Error(message);
}
1.4 함수 타입 선언 방식
함수의 타입은 여러 가지 방식으로 선언할 수 있습니다. 다만 이렇게 함수를 명시적으로 선언하는 경우는 복잡하여 간략화 해서 사용하는 경우가 많습니다.
// 함수 표현식
const add: (x: number, y: number) => number = function(x, y) {
return x + y;
};
// 화살표 함수
const multiply: (x: number, y: number) => number = (x, y) => x * y;
// 타입 별칭 사용
type MathFunction = (x: number, y: number) => number;
const divide: MathFunction = (x, y) => x / y;
// 함수 표현식
const add: (x: number, y: number) => number = function(x, y) {
return x + y;
};
// 화살표 함수
const multiply: (x: number, y: number) => number = (x, y) => x * y;
// 타입 별칭 사용
type MathFunction = (x: number, y: number) => number;
const divide: MathFunction = (x, y) => x / y;
간략화해서 사용하는 경우는 아래와 같습니다.
// 1. 가장 기본적인 방식
function add(x: number, y: number): number {
return x + y;
}
// 2. 화살표 함수 - 타입 추론 활용
const add = (x: number, y: number) => x + y;
// 반환 타입을 명시하지 않아도 타입스크립트가 알아서 추론합니다
// 3. 타입 별칭을 사용할 때 (재사용이 필요한 경우)
type Calculator = (x: number, y: number) => number;
const add: Calculator = (x, y) => x + y;
const multiply: Calculator = (x, y) => x * y;
// 1. 가장 기본적인 방식
function add(x: number, y: number): number {
return x + y;
}
// 2. 화살표 함수 - 타입 추론 활용
const add = (x: number, y: number) => x + y;
// 반환 타입을 명시하지 않아도 타입스크립트가 알아서 추론합니다
// 3. 타입 별칭을 사용할 때 (재사용이 필요한 경우)
type Calculator = (x: number, y: number) => number;
const add: Calculator = (x, y) => x + y;
const multiply: Calculator = (x, y) => x * y;
1.5 함수 오버로딩
TypeScript에서는 같은 함수명으로 다른 매개변수를 처리할 수 있습니다.
function greet(name: string): string;
function greet(age: number): string;
function greet(value: string | number): string {
if (typeof value === "string") {
return `Hello, ${value}!`;
} else {
return `You are ${value} years old!`;
}
}
console.log(greet("TypeScript")); // "Hello, TypeScript!"
console.log(greet(25)); // "You are 25 years old!"
function greet(name: string): string;
function greet(age: number): string;
function greet(value: string | number): string {
if (typeof value === "string") {
return `Hello, ${value}!`;
} else {
return `You are ${value} years old!`;
}
}
console.log(greet("TypeScript")); // "Hello, TypeScript!"
console.log(greet(25)); // "You are 25 years old!"
다만 이렇게 선언하지 않고 아래처럼 작성하는 것도 가능합니다. 이렇게 유니온 타입으로 선언하는 것이 더 일반적입니다.
function greet(value: string | number): string {
if (typeof value === "string") {
return `Hello, ${value}!`;
} else {
return `You are ${value} years old!`;
}
}
console.log(greet("TypeScript")); // "Hello, TypeScript!"
console.log(greet(25)); // "You are 25 years old!"
function greet(value: string | number): string {
if (typeof value === "string") {
return `Hello, ${value}!`;
} else {
return `You are ${value} years old!`;
}
}
console.log(greet("TypeScript")); // "Hello, TypeScript!"
console.log(greet(25)); // "You are 25 years old!"
1.6 기본 매개변수
매개변수에 기본값을 설정할 수 있습니다.
function createUser(name: string, age: number = 20): object {
return {
name,
age
};
}
console.log(createUser("Alice")); // { name: "Alice", age: 20 }
console.log(createUser("Bob", 25)); // { name: "Bob", age: 25 }
function createUser(name: string, age: number = 20): object {
return {
name,
age
};
}
console.log(createUser("Alice")); // { name: "Alice", age: 20 }
console.log(createUser("Bob", 25)); // { name: "Bob", age: 25 }
💡 기본 매개변수를 사용하면 해당 매개변수는 자동으로 선택적 매개변수가 되며, 타입은 명시한 타입이 됩니다.
2. 연습문제
2.1 매개변수와 반환 타입 지정
다음 함수의 매개변수와 반환 타입을 지정해보세요.
// 두 숫자를 받아서 그 합을 반환하는 함수
function add(x, y) {
return x + y;
}
// 문자열을 받아서 콘솔에 출력하는 함수
function printMessage(message) {
console.log(message);
}
// 두 숫자를 받아서 그 합을 반환하는 함수
function add(x, y) {
return x + y;
}
// 문자열을 받아서 콘솔에 출력하는 함수
function printMessage(message) {
console.log(message);
}
2.2 함수의 오버로딩
아래 조건을 만족하는 JavaScript 함수를 만들어보세요.
- add 함수를 만듭니다.
- 값은 1개 들어올 수 있고, 2개가 들어올 수도 있고, 최대 3개가 들어올 수 있습니다.
- 오버로딩을 사용합니다.
2.3 함수와 타입
아래 조건을 만족하는 JavaScript 함수, TypeScript 함수를 만들어보세요.
- 함수는 두개의 인자를 받습니다.
- products: 제품 목록 배열 (각 제품은 name과 price 프로퍼티를 가집니다)
- minPrice: 필터링 할 최소 가격, 입력이 없다면 0으로 설정합니다.
- 최종적으로 필터링하고 정렬된 제품의 이름 목록을 반환합니다.
- 완성된 함수를 타입스크립트를 이용해 변경해봅시다.
예를 들어 데이터는 아래와 같습니다.
const products = [{ name: '칫솔', price: 3000 }, { name: '사과', price: 4000 }, { name: '배', price: 6000 }, { name: '바나나', price: 3500 }]
const minPrice = 3000
const products = [{ name: '칫솔', price: 3000 }, { name: '사과', price: 4000 }, { name: '배', price: 6000 }, { name: '바나나', price: 3500 }]
const minPrice = 3000
3. 연습문제 정답
3.1 매개변수와 반환 타입 지정
function add(x: number, y: number): number {
return x + y;
}
function printMessage(message: string): void {
console.log(message);
}
function add(x: number, y: number): number {
return x + y;
}
function printMessage(message: string): void {
console.log(message);
}
3.2 함수의 오버로딩
- 이렇게 했을 때 장점은 명확한 타입 문서화를 할 수 있다는 것입니다. 유니온을 쓰셔도 됩니다.
// 함수 오버로딩 시그니처
function add(x: number): number;
function add(x: number, y: number): number;
function add(x: number, y: number, z: number): number;
// 함수 구현
function add(x: number, y?: number, z?: number): number {
if (y === undefined) return x;
if (z === undefined) return x + y;
return x + y + z;
}
// 함수 오버로딩 시그니처
function add(x: number): number;
function add(x: number, y: number): number;
function add(x: number, y: number, z: number): number;
// 함수 구현
function add(x: number, y?: number, z?: number): number {
if (y === undefined) return x;
if (z === undefined) return x + y;
return x + y + z;
}
3.3 함수와 타입
// 바닐라 JS
const products = [{ name: '칫솔', price: 3000 }, { name: '사과', price: 4000 }, { name: '배', price: 6000 }, { name: '바나나', price: 3500 }];
const minPrice = 3000;
function filterAndSortProducts(products, minPrice) {
return products
.filter(product => product.price >= minPrice)
.sort((a, b) => b.name.localeCompare(a.name))
.map(item => item.name);
}
const result = filterAndSortProducts(products, minPrice);
console.log(result);
//TypeScript 변환 후
const products = [{ name: '칫솔', price: 3000 }, { name: '사과', price: 4000 }, { name: '배', price: 6000 }, { name: '바나나', price: 3500 }];
const minPrice = 3000;
function filterAndSortProducts2(
products: { name: string; price: number }[],
minPrice: number
): string[] {
return products
.filter((product) => product.price >= minPrice)
.sort((a, b) => a.name.localeCompare(b.name))
.map((product) => product.name);
}
filterAndSortProducts2(products, minPrice);
// 바닐라 JS
const products = [{ name: '칫솔', price: 3000 }, { name: '사과', price: 4000 }, { name: '배', price: 6000 }, { name: '바나나', price: 3500 }];
const minPrice = 3000;
function filterAndSortProducts(products, minPrice) {
return products
.filter(product => product.price >= minPrice)
.sort((a, b) => b.name.localeCompare(a.name))
.map(item => item.name);
}
const result = filterAndSortProducts(products, minPrice);
console.log(result);
//TypeScript 변환 후
const products = [{ name: '칫솔', price: 3000 }, { name: '사과', price: 4000 }, { name: '배', price: 6000 }, { name: '바나나', price: 3500 }];
const minPrice = 3000;
function filterAndSortProducts2(
products: { name: string; price: number }[],
minPrice: number
): string[] {
return products
.filter((product) => product.price >= minPrice)
.sort((a, b) => a.name.localeCompare(b.name))
.map((product) => product.name);
}
filterAndSortProducts2(products, minPrice);