Typescript Function

함수는 JavaScript의 모든 응용 프로그램을 구성하는 기본 요소입니다. 추상화 계층, 클래스 모방, 정보 숨김 및 모듈을 함수로 구성합니다. TypeScript에서는 클래스, 네임 스페이스 및 모듈이 있지만 함수는 여전히 작업 수행 방법을 설명하는 데 중요한 역할을합니다. TypeScript는 표준 JavaScript 함수에 몇 가지 새로운 기능을 추가하여보다 쉽게 작업 할 수 있도록합니다.

Function

JavaScript에서와 마찬가지로, TypeScript 함수는 명명된 함수 또는 익명 함수로 만들 수 있습니다. 이를 통해 API에서 함수 목록을 작성하든 한번 사용하는 함수를 다른 함수로 이전 할 것인지에 관계없이 응용 프로그램에 가장 적합한 방법을 선택할 수 있습니다.

자바 스크립트에서 이러한 두 가지 접근 방식을 빠르게 요약하면 다음과 같습니다.

1
2
3
4
5
6
7
// Named function
function add(x, y) {
return x + y;
}
// Anonymous function
let myAdd = function(x, y) { return x+y; };

JavaScript와 마찬가지로 함수는 외부의 변수를 참조 할 수 있습니다. 이러한 참조를 변수를 capture한다고 말합니다.

1
2
3
4
let z = 100;
function addToZ(x, y) {
return x + y + z;
}

Function Type

Typing the function

1
2
3
4
5
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x+y; };

각 parameter에 type을 추가하고 return type을 추가해서 함수 자체에도 type을 추가할 수 있습니다. TypeScript는 return 문을보고 return type을 파악할 수 있으므로 많은 경우에 생략할 수 있습니다.

Writing the function type

함수를 입력 했으므로 각 함수 유형을 살펴보고 함수의 전체 유형을 작성해 보겠습니다.

1
2
let myAdd: (x:number, x:number) => number;
myAdd = function(x: number, y: number): number { return x + y; };

함수의 type은 parameter type과 return type 두 부분으로 구성됩니다. 둘 사이는 굵은 화살표 (=>)로 구분합니다.

type 추론

할당문에서 한쪽에만 type을 명시하더라도 TypeScript 컴파일러는 다른쪽의 type을 알 수가 있습니다.

1
2
3
4
let myAdd = function(x: number, y: number): number { return x + y; };
let myAdd: (baseValue:number, increment:number) => number =
function(x, y) { return x + y; };

이런 type 추론을 “contextual typing”라고 합니다.

Optional and Default Parameter

TypeScript에서는 함수를 호출할때는 모든 파라미터를 전달해야 합니다. 이것은 null이나 undefined가 주어질 수 없다는 것을 의미하는 것이 아니라, 함수가 호출 될 때 컴파일러는 사용자가 각 매개 변수에 값을 제공했는지 확인합니다. 컴파일러는 또한 이 매개 변수가 함수로 전달되는 유일한 매개 변수라고 가정합니다. 즉, 함수에 주어진 아규먼트의 수는 함수가 예상하는 파라미터의 수와 일치해야합니다.

1
2
3
4
5
6
7
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // 에러
let result2 = buildName("Bob", "Adams", "Sr."); // 에러
let result3 = buildName("Bob", "Adams");

자바 스크립트에서 모든 매개 변수는 선택 사항이며 사용자는 적합하다고 판단되는 매개 변수를 제외할 수 있습니다. 제외된 파라미터는 undefined가 됩니다. TypeScript에서는 파라미터의 끝에 ?를 붙여서 선택적 파라미터라고 기술할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob");
let result2 = buildName("Bob", "Adams", "Sr."); // 에러
let result3 = buildName("Bob", "Adams");

모든 선택적 파라미터는 필수 파라미터 다음에 기술해야 합니다.

TypeScript에서 아큐먼트를 전달하지 않거나 undefined를 전달할때 파라미터에 할당되는 값을 설정할 수 있습니다. 이를 default 파라미터라고합니다.

1
2
3
4
5
6
7
8
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // "Bob Smith"
let result2 = buildName("Bob", undefined); // "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr.");
let result4 = buildName("Bob", "Adams");

optional 파라미터와 default 파라미터는 동일한 type이 됩니다.

다음 두 예는 (firstName : string, lastName ?: string) => string로 같은 type입니다.

1
2
3
function buildName(firstName: string, lastName?: string) {
// ...
}
1
2
3
function buildName(firstName: string, lastName = "Smith") {
// ...
}

optional 파라미터와 달리 default 파라미터는 필수 파라미터 앞에 올 수도 있습니다.

1
2
3
4
5
6
7
8
function buildName(firstName = "Will", lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // 에러
let result2 = buildName("Bob", "Adams", "Sr."); // 에러
let result3 = buildName("Bob", "Adams"); // returns "Bob Adams"
let result4 = buildName(undefined, "Adams"); // returns "Will Adams"

Rest Parameter

파라미터의 갯수가 일정하게 정해지 있지 않을 때 JavaScript에서는 arguments를 사용하며 TypeScript에서는 ...를 사용합니다.

1
2
3
4
5
6
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
let employeeName = buildNameFun("Joseph", "Samuel", "Lucas", "MacKinzie");
console.log(employeeName);

this

JavaScript에서 this를 사용하는 방법을 배우는 것은 통과 의식과도 같은 것 입니다. TypeScript는 JavaScript의 superset이므로 TypeScript 개발자도 또한 this 사용하는 방법을 배워야 하고 올바르게 사용되지 않는 시점을 찾는 방법을 배워야합니다. 다행스럽게도 TypeScript를 사용하면 두 가지 기술을 사용하여 this의 잘못된 용도를 잡을 수 있습니다. JavaScript에서 this가 어떻게 작동하는지 알아야 할 필요가 있다면 Yehuda Katz의 Understanding JavaScript Function Invocation과 “this”를 먼저 읽으십시오. 여기서는 기본 사항 만 다루겠습니다.

this and arrow function

자바 스크립트에서 이것은 함수가 호출 될 때 설정된 변수입니다. 이것은 매우 강력하고 유연한 기능이지만 항상 함수가 실행되는 상황에 대해 알 필요가 있습니다. 이것은 특히 함수를 반환하거나 인수를 함수로 전달할 때 혼란 스럽습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
return function() {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card: " + pickedCard.card + " of " + pickedCard.suit);

createCardPicker는 그 자체로 함수를 반환하는 함수입니다. 예제를 실행하면 오류가 발생합니다. 이것은 createCardPicker에 의해 생성 된 함수인 cardPicker()에서 사용되는 thisdeck 객체 대신 window로 설정되기 때문입니다. (참고 : strict mode에서는 thiswindow가 아니고 undefined가 됩니다.)

이 문제를 해결하기 위해서는 ECMAScript 6 arrow syntax를 사용해서 함수식을 변경합니다. arrow function은 함수가 호출되는 곳이 아닌 하뭇가 생성되는 곳에서 this를 capture합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card: " + pickedCard.card + " of " + pickedCard.suit);

this 파라미터

this.suits [pickedSuit]의 type은 any입니다. this가 객체 리터럴 내부의 함수 표현식에서 비롯 되었기 때문입니다.
CardDeck 인터페이스를 추가하면 type을 명확하고 쉽게 재사용 할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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) {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card: " + pickedCard.card + " of " + pickedCard.suit);

this parameter in callback

나중에 콜백 할 라이브러리에 함수를 전달할 때 콜백에서이 오류를 실행할 수도 있습니다. 콜백을 호출하는 라이브러리는 일반 함수처럼 호출하기 때문에 이것은 정의되지 않습니다. 일부 작업에서는이 매개 변수를 사용하여 콜백으로 인한 오류를 방지 할 수 있습니다. 먼저, 라이브러리 제작자는 다음과 같이 콜백 유형에 주석을 달아야합니다.

1
2
3
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}

this: voidaddClickListener 함수의 파라미터 onclickthis type을 필요로 하지 않는 함수라는 것을 기술합니다.

둘째, this를 파라미터에 명시합니다.

1
2
3
4
5
6
7
8
9
10
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// oops, used this here. using this callback would crash at runtime
this.info = e.message;
};
}
let uiElement: UIElement;
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 에러

this를 파라미터에 추가 하면 onClickBadHandler의 인스턴스에서 호출되어야 한다는 사실을 명시합니다. 그런 다음 TypeScript는 addClickListenerthis: void 파라미터가 있는 함수가 필요하다는 것을 감지합니다. 오류를 수정하려면 this의 type을 변경합니다.

1
2
3
4
5
6
7
8
9
class Handler {
info: string;
onClickGood(this: void, e: Event) {
console.log('clicked!');
}
}
let uiElement: UIElement;
let h = new Handler();
uiElement.addClickListener(h.onClickGood);

onClickGood 함수에서 thisvoid로 지정했기 때문에 addClickListener에 전달할 수 있습니다. 물론 이것은 this.info를 사용할 수 없다는 것을 의미합니다. 둘 다 원한다면 arrow function을 사용해야합니다.

1
2
3
4
class Handler {
info: string;
onClickGood = (e: Event) => { this.info = e.message }
}

이것은 arrow function가 this를 capture하지 않기 때문입니다. 그래서 여러분은 항상 this: void를 기대하는 무언가에 전달할 수 있습니다. 단점은 Handler type의 객체 당 하나의 arrow function가 생성된다는 것입니다. 한편, 메소드는 한 번만 생성되어 Handler의 prototype에 결합됩니다. 메소드는 Handler type의 모든 객체간에 공유됩니다.

Overload

JavaScript는 본질적으로 매우 동적인 언어입니다. 단일 JavaScript 함수가 전달된 argument의 형태를 기반으로 여러 type의 객체를 반환하는 경우는 흔합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x): 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
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
console.log("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
console.log("card: " + pickedCard2.card + " of " + pickedCard2.suit);

여기서 pickCard 함수는 argument에 따라서 두 가지 다른 것을 반환합니다. deck를 나타내는 객체를 전달하면 함수가 카드를 선택합니다. 사용자가 카드를 선택하면 우리는 그들이 선택한 카드를 알려줍니다. type 시스템으로 어떻게 기술 할 수 있습니까요?

그 대답은 과부하 목록과 동일한 함수에 대해 여러 함수 유형을 제공하는 것입니다. 이 목록은 컴파일러가 함수 호출을 해결하는 데 사용할 것입니다. pickCard가 받아 들일 수있는 부분과 반환하는 부분을 설명하는 오버로드 목록을 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): 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
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

이 변경으로 오버로드가 이제 pickCard 함수에 대한 유형 확인 된 호출을 제공합니다.

컴파일러가 올바른 typecheck를 선택하도록하기 위해 기본 JavaScript와 비슷한 과정을 거칩니다. 오버로드 목록을 보고 제공된 매개 변수를 사용하여 함수를 호출하는 첫 번째 오버로드 시도를 계속합니다. 일치하는 항목을 찾으면 올바른 오버로드로써 이 오버로드를 선택합니다. 이런 이유로 가장 구체적인 것이 먼저 나열되어야 합니다.

pickCard(x): any 함수는 오버로드 리스트의 일부가 아니므로 오브젝트를 취하는 오버로드와 숫자를 취하는 오버로드의 두 가지 오버로드 만 있습니다. 다른 매개 변수 유형으로 pickCard를 호출하면 오류가 발생합니다.

Share