Typescript-Function

Functions

Function 在 Javascript 中是很基本的型態,可以用在隱藏資訊,或攥寫模組等功用

Javascript 中基本的 Function 有兩種

1
2
3
4
5
function add(x, y){
return x+y;
}

let myAdd = function(x, y){return x+y;}

在 Javascript 中的 Function 可以使用外部的變數,這個行為叫做 capture

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

使用 TypeScript 寫一個最基本的範例

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;};

完整的 Function Type 範例

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

上述範例中輸入的參數可以定義類別,同時也宣告 Function 回傳的類別,並且有兩個相同的宣告類別,

但是做事在一開始就宣告完整的 Function 類別,那之後就可以省略

範例

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

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

Optional and Default Parameters

TypeScript 中指定每個參數不代表他們不能是 null 或是 undefined

1
2
3
4
5
6
7
function buildName(firstName: string, lastName: string){
return firstName + ' ' + lastName;
}

// let result1 = buildName("Bob"); // error, too few parameters
// let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right

在 Javascript 中每一個 Function 都是非必要的,當你沒有輸入的時候值都會是 undefinedTypeScript 有宣告的都是必要的,但是也提供一個 ? 來宣告非必要

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"); // works correctly now
// let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right

TypeScript 非必要參數必須要在 必要參數的後面, TypeScript 也可以提供預設值的設定

範例

1
2
3
4
5
6
7
8
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}

let res1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let res2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
// let res3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let res4 = buildName("Bob", "Adams"); // ah, just right

上面的範例中可以看到,你可以先預設值給予預設值之後就不是必要參數,因為當你沒有輸入該參數的時候也會有預設輸入不會影響程式執行

Rest Parameters

ES6 也有一種特性 Rest

而這個特性在 TypeScript 也可以應用在 Function 之中,當你不知道之後輸入的參數值總共有幾個,可以利用這個特性將所有後面輸入的參數值組合成一個陣列

1
2
3
4
5
6
7
function buildName4(firstName: string, ...restOfName: string[]) {
console.log(`firstName: ${firstName}`);
console.log(`restOfName: ${restOfName}`);
return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName4("Joseph", "Samuel", "Lucas", "MacKinzie");

this

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();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

上述範例是可以編譯的,但是在執行的時候會有錯誤

因為在執行照 createCadrPicker 的時候會找不到 suits 這個 Function 因為這是 this的作用域的問題

因為對 Javascript 來說 Function 也是物件,所以在上述範例中的 createCadrPicker 中的 this 是指這個 createCadrPicker Function 本身,但是這個 Function 並沒有 suits 這個屬性,所以他會找到 undefeind 在後面 this.suits[pickedSuit] 的時候因為 undefined 在 Javascript 並不是物件,所以就會造成這個錯誤

所以我們可以把這個範例做一些修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
// NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
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();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

其實我們做的修改只是將 function(){} 修改為 () => {} 但是因為在 Javascript 中的 arrow function的特性 所以他找到的 this 是指 desk 這個物件,就可以指導 suits 這個屬性這個屬性

this parameters

但是在上面範例中的 this 的型別依舊是 any 如果我們希望在 Function 定義 this 的型態就要將這個宣告放在 Function 的第一個參數

1
2
3
function f(this: void){
// ....
}

加上兩個 interfaceCardDeck

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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),
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};
}
}
}

interface 中的 createCardPicker 有宣告了 this 的型態是 Deck 而不是 any 所以 --noImplicitThis 不會有錯誤

this parameters in callbacks

你在 callback 中使用 this 的話依舊會產生一些錯誤,因為 this 會是 undefine 你可以宣告一個 interface 來避免這種錯誤

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

this: void 代表 addClickListener 預計 onClick 是一個 Fucntion 並沒有 this 的類別

1
2
3
4
5
6
7
8
9
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 h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!

Overloads

Javascript 是一個動態繼承的程式語言,一個函式藉由輸入值得到不同的回傳值是十分常見的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x): any {
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
} 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 pickedCard01 = myDeck[pickCard(myDeck)];
console.log("card: " + pickedCard01.card + " of " + pickedCard01.suit);
console.log(`pickedCard01: ${JSON.stringify(pickedCard01)}`);
let pickedCard02 = pickCard(15);
console.log("card: " + pickedCard02.card + " of " + pickedCard02.suit);
console.log(`pickedCard02: ${JSON.stringify(pickedCard02)}`);

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 suits02 = ["hearts", "spades", "clubs", "diamonds"];

function pickCard1(x: { suit: string; card: number }[]): number;
function pickCard1(x: number): { suit: string; card: number };
function pickCard1(x): any {
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
} else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits02[pickedSuit], card: x % 13 };
}
}

let myDeck02 = [
{ suit: "diamonds", card: 2 },
{ suit: "spades", card: 10 },
{ suit: "hearts", card: 4 }
];
let pickedCard001 = myDeck02[pickCard1(myDeck02)];
console.log("card: " + pickedCard001.card + " of " + pickedCard001.suit);

let pickedCard002 = pickCard1(15);
console.log("card: " + pickedCard002.card + " of " + pickedCard002.suit);

為了要讓 TypeScript 編譯的時候能夠選擇正確的型態,會宣告兩個不同的 PickCard1 然後分別宣告不同的 parmeter 而產生的不同的 response,然後在最後真正宣告 function pickCard1():any 設定回傳值是 any
,之後再真正使用 pickCard1 的時候就會依據不同的 parmeter 檢查不同的形態和 response

文章目录
  1. 1. Functions
    1. 1.0.1. Optional and Default Parameters
    2. 1.0.2. Rest Parameters
    3. 1.0.3. this
    4. 1.0.4. this parameters
      1. 1.0.4.1. this parameters in callbacks
    5. 1.0.5. Overloads
|