이전까지 타입스크립트에서 사용되는 다양한 타입들에 대해서 알아보았다. 이제는 함수를 어떻게 사용하는지 알아보자.
자바스크립트에서 함수는 일급 객체다. 즉 객체를 다루듯이 함수를 변수에 할당하거나, 함수를 다른 함수로 전달하거나 함수에서 함수를 반환하거나, 객체와 프로토타입에 할당하거나 하는 작업들을 할 수 있다.
function add(a: number, b: number) {
return a + b;
}
보통 함수 매개변수의 타입은 명시적으로 정의한다.
타입스크립트는 대부분의 상황에서 타입을 추론하지만 매개변수에는 타입을 추론하지 않는다. 반환타입은 원하면 명시해줄 수 있다.
function add(a: number, b: number) : number {
return a + b;
}
타입스크립트에서 함수를 선언하는 방법은 다섯가지이다.
// 1.
function greet(name: string) {
return 'hello' + name;
}
// 2.
let greet2 = function (name: string) {
return 'hello' + name;
}
// 3.
let greet3 = (name : string) => {
return 'hello' + name;
}
// 4.
let greet4 = (name : string) => 'hello' + name;
// 5.
let greet5 = new Function('name', 'return "hello " +, name');
함수에서는 ?
연산자를 이용해서 선택적 매개변수를 지정할 수 있고 기본 매개변수를 지정할 수도 있다.
function log(message: string, userId?:string) {
let time = new Date().toLocaleTimeString();
}
function log2(message: string, userId = 'not signed in') {
let time = new Date().toLocaleTimeString();
}
타입스크립트에서도 arguments
객체를 통해 나머지 매개변수를 받을 수 있지만 ES6와 혼용할 때 안전하지 않다. 그러므로 rest 파라미터를 쓰는 것이 안전하다.
function sumVariadicSafe(...numbers :number[]):number {
return numbers.reduce((total, n) => total + n, 0);
}
sumVariadicSafe(1,2,3); // 6
함수의 전체 타입을 표현하는 방법을 알아보자.
function add(a: number, b: number) : number {
return a + b;
}
sum은 두 개의 number
를 인수로 받아 한 개의 number
를 반환하는 함수이다.
(a:number, b:number ) => number
이 코드는 호출 시그니처 또는 타입 시그니처라고 부른다. 함수 호출 시그니처는 타입 수준 코드, 즉 값이 아닌 타입 정보만 포함한다.
실제로 사용할 땐 다음과 같이 사용한다.
type Log = (message : string, userId?: string) => void;
let log: Log = ( message, userId='Not signed in') => {
let time = new Date().toISOString();
console.log(time, message, userId);
}
// 단축형 호출 시그니처
type Log = (message : string, userId?: string) => void;
// 전체 호출 시그니처
type Log = {
(message : string, userId?:string) : void;
}
Log
처럼 간단한 상황이라면 단축형을 주로 활용하고 복잡한 상황이면 전체 호출 시그니처를 사용하면 되겠다. 오버로딩이 좋은 예시이다.
reserve
함수를 구현해보자.
type Reserve = {
(from: Date, to: Date, destination: string): Reservation;
}
let reserve : Reserve = (from, to, destination) => {
// ...
}
발리로 여행가려는 고객이 있다면 from 과 to
에는 날짜, destination
은 'bali'로 설정해 사용할 것이다.
다음처럼 편도 여행을 위한 api를 만들 수도 있다.
type Reserve = {
(from: Date, to: Date, destination: string): Reservation;
(from: Date, destination: string): Reservation;
}
실행하려고 하면 이런 에러가 발생하는 것을 확인할 수 있다.
type을 정의할 때는 유니온으로 평가되지만 구현할 때는 직접 합쳐서 구현해야 한다.
type Reserve = {
(from: Date, to: Date, destination: string): Reservation;
(from: Date, destination: string): Reservation;
}
let reserve: Reserve = (
from: Date, toOrDestination: Date | string, destination?: string) =>
{
// ...
}
filter
함수를 구현하면서 알아보자.
function filter(array, f) {
let result = [];
for (let i =0; i< array.length; i++) {
let item = array[i];
if (f(item)) {
result.push(item);
}
}
return result;
}
filter([1,2,3,4], _ => _ <3); // [1,2];
// 1단계
type Filter = {
(array: unknown, f:unknown) => unknown[];
}
// 2단계
type Filter = {
(array: number[], f: (item: number) => boolean): number[];
}
number[]
로 바꾸는 건 어렵지 않지만 number배열 이외에도 여러 타입의 배열들을 가질 수 있도록 오버로드를 이용해보자.
// 3단계
type Filter = {
(array: number[], f: (item: number) => boolean): number[];
(array: string[], f: (item: number) => boolean): string[];
}
객체 배열을 전달하고 싶다면 object[]
를 추가하면 될 것 처럼 보이지만 쉽게 해결되지는 않는다. object
라는 타입은 앞에서도 살펴보았듯이 존재한다는 사실말고는 아무것도 제공하지 않기 때문이다.
이럴 때는 제네릭을 쓰면 문제를 해결할 수 있다
type Filter = {
<T>(array: T[], f: (item: T) => boolean): T[]
}
"filter함수는 T라는 제네릭 타입 매개변수를 사용한다. 이 타입은 무엇인지 지금은 알 수 없으니 누군가 filter를 호출할 때마다 타입스크립트가 타입을 추론해주길 바란다" 라는 뜻이 담겨져 있다고 할 수 있겠다
꺽쇠 괄호로 제네릭 타입 매개변수임을 선언한다. 꺽쇠 기호를 추가하는 위치는 한정되어 있다. 아무데나 할 수는 없다..
함수의 매개변수가 함수를 호출할 때 건네진 인수로 매번 다시 한정되듯 T도 filter를 호출할 때마다 새로운 타입으로 한정된다.
type Filter = {
<T>(array: T[], f: (item: T) => boolean) : T[]
}
let filter: Filter = (array, f) => // ...
// T number로 한정됨
filter([1,2,3], _ => _ > 2);
// T string으로 한정됨
filter(['a','b']), _ => _ !== 'b');
첫 번째 제네릭 추론 과정을 글로 설명해보자.
filter의 타입 시그니처를 통해 array가 타입이 T인 요소들로 이루어진 배열임을 알게 된다.
전달된 인수 array[1,2,3]
을 통해 T가 number라는 사실을 알게된다.
이제 모든 T를 number 타입으로 대치한다. 따라서 매개변수 f: (item:T) => boolean
은 f: (item : number) => boolean
이 되고 반환 타입 T[]는 number[]가 된다.
📍 제네릭은 함수의 기능을 일반화하여 설명할 수 있는 도구이다. 제네릭을 사용하면 재사용성을 높이고 간결하게 유지하는 데 도움을 준다.
click 이나 mousedown 같은 DOM 이벤트를 설명하는 MyEvent 타입을 정의해보자.
type MyEvent<T> = {
target : T
type : string
}
타입 별칭에서는 타입 별칭명과 =
기호 사이에만 제네릭을 선언할 수 있다.
type ButtonEvent = MyEvent<HTMLButtonElement>
제네릭 타입에도 기본값을 설정해줄 수 있다.
type MyEvent<T> = {
target : T
type : string
}
특정요소 타입을 알 수 없는 떄를 대비해 기본값을 추가해보자.
type MyEvent<T = HTMLElement> = {
target : T
type: string
}