https://www.typescriptlang.org/docs/handbook/functions.html
JavaScript에서와 같이 TypeScript에서는 함수들이 함수의 이름이 존재하는 named function 혹은 함수의 이름이 없는 anonymous function으로 생성될 수 있다. 또한 함수들은 함수 바깥에 있는 변수들을 사용할 수 있고, 이 변수를 captured variable이라고 한다.
let z = 100; // captured variable
// Named function
function add(x, y) {
return x + y + z;
}
// Anonymous function
let myAdd = function (x, y) {
return x + y + z;
};
함수의 parameter와 return 값에 타입을 정할 수 있다.
function add(x: number, y: number): number {
return x + y;
}
함수 전체의 타입은 (parameter name: type list) => return type 형식으로 나타낸다. parameter들의 이름은 정확하지 않아도 type만 정확하면 정확한 함수 타입으로 간주된다.
let myAdd: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
TypeScript 컴파일러는 함수의 타입 혹은 함수의 정의 둘 중 하나에만 타입을 선언해도 추론을 통해 나머지의 타입을 알아내기 때문에 한 쪽에만 타입을 적어도 괜찮다. 이렇게 타입을 추론하는 것을 contextual typing이라고 한다.
// 함수의 정의에 파라미터와 리턴 타입 선언
let myAdd = function (x: number, y: number): number {
return x + y;
};
// 함수의 타입에 파라미터와 리턴 타입 선언
let myAdd2: (baseValue: number, increment: number) => number = function (x, y) {
return x + y;
};
함수에 전달되는 인자의 수와 함수의 파라미터의 수는 언제나 같아야 한다. 하지만 마지막 파라미터의 이름 뒤에 물음표(?)를 추가하여 optional parameter로 선언하면 함수 인자를 생략해도 된다.
function buildName(firstName: string, lastName?: string) {
// ...
}
아니면 함수의 선언 부분에서 파라미터에 기본값을 넣어주어 default parameter로 선언하면 생략되었을 때 기본값을 사용하게 된다. 하지만 optional parameter와 다르게 default parameter는 마지막에 오지 않아도 되지만 사용할 때 default parameter의 자리에 "undefined"를 넣어주어야 한다.
function buildName(firstName = "Will", lastName : string) {
// ...
}
let result = buildName(undefined, "Bob");
여러 인자들을 하나의 그룹(배열)으로 받는 파라미터가 rest parameter이다. parameter의 이름 앞에 "..."을 붙여 사용하고 타입은 배열 타입이다. rest parameter가 받는 인자의 개수는 정해지지 않아 0개일 수도 있고, 100개일 수도 있다.
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
// employeeName will be "Joseph Samuel Lucas MacKinzie"
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
this and Arrow Functions
JavaScript에서 this는 함수가 실행되었을 때 설정되는 변수이다. 따라서 항상 함수가 실행되는 context를 알아야 정상적인 실행이 가능하지만 context에 따라 this가 의미하는 객체가 달라질 수 있어 혼란이 생길 수 있다. ex) this를 함수를 가진 객체 아닌 window로 설정
이 문제는 arrow function을 사용하면 해결할 수 있다. arrow function은 함수가 실행된 시점이 아닌 함수가 생성된 시점에서 this를판단하기 때문이다.
하지만 객체 내부의 함수에서 this를 그냥 사용하면 this의 타입이 정해지지 않았기 때문에 타입이 any로 설정된다. 따라서 함수 맨 첫 인자로 this를 주어 명시적으로 this를 어떤 타입으로 사용할 것인지를 나타내야 한다.
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
}
let deck: Deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
// NOTE: The function now explicitly specifies that its callee must be of type Deck
createCardPicker: function (this: Deck) { // Deck 타입의 this를 사용할 것을 명시
return () => { // arrow function을 사용하여 this 사용
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
// this.suits[pickedSuit]의 타입이 any가 아닌 string이 됨
};
},
};
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
함수가 필요할 때 나중에 실행되는 callback 함수에서 this를 일반 함수에서처럼 사용하면 this가 undefined가 된다. 이 문제를 해결하기 위해서는 마찬가지로 this의 타입을 명시해주거나 arrow function을 사용하면 된다.
JavaScript에서는 인자의 타입에 따라 여러 타입의 값을 return할 수 있다. TypeScript에서는 함수 오버로딩을 사용하여 같은 기능을 구현할 수 있다.
function pickCard(x: { suit: string; card: number }[]): number; // function overloading 1
function pickCard(x: number): { suit: string; card: number }; // function overloading 2
function pickCard(x: any): any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
// x의 타입이 객체일 경우 number 반환 - function overloading 1
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
// x의 타입이 number일 경우 {string, number}객채 반환 - function overloading 2
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}