本篇將會了解不同種類的 Function 在 TypeScript 中的寫法和變化
Function 分成幾大部分講解
又稱「函式陳述式 (Function Statement)」
我們先把函式拆成兩部份來看
在 TypeScript 中,可以為函式的「參數」和「回傳值」定義型別,若不寫則會被推斷為 any
以下方 test
為例,相加的結果自然也是 any
a
, b
加上型別註釋function add(a: string, b: string) {
return a + b;
}
add('1', '4') // ✅ pass
add(1, '2') // ❌ Argument of type 'number' is not assignable to parameter of type 'string'. 第一個參數必須要是字串,非數字
void
; 若有回傳值,則在參數後定義即可,如下例:function f2(a: number, b: number): void {
document.querySelector('body').append(document.createElement('div'));
}
// 下方會被自動推斷為 function noop(): void
function noop() {
return;
}
function f3(a: number, b: number): number { // 最右邊的 : number 代表回傳值型別
return a + b;
}
// return 4 為數字,回傳值會自動被推斷為 : number
function getNum(a, b) {
return 4;
}
假設回傳的是一個 Promise
,就必須使用內建的 Promise
型別。如下方的 Promise<number>
,這也是一個泛型(之後章節會談到),代表函式在被呼叫後會回傳 Promise,而 Promise 被解析後會回傳一個型別為 number
的值
⭐️ 補充:Promise
雖是一個物件型別,但在 TypeScript 中,它是一個帶有「泛型參數」的特殊物件型別,專門用來處理非同步。因此,直接將 Promise 定義為 :object
會喪失 Promise 的特殊語義和型別安全性
async function getFavoriteNumber(): Promise<number> {
return 26;
}
async function fetchData(): Promise<object> {
return { name: "Hannah", age: 18 };
}
先來回顧一下 JavaScript
函式表達式的名稱可以是「具名的」或「匿名的」
函式陳述式的名稱必須是「具名的」
這是典型的「函式表達式」
let f3 = function(a: string, b: string): string {
return a + b;
}
範例內容取自官方文件
const names = ["Alice", "Bob", "Eve"];
names.forEach(function (s) { // ❓ 猜猜看 s 會被推斷為什麼型別
console.log(s.toUpperCase());
});
答案是
此匿名函式會在呼叫 forEach
的時候被執行,參數 s
會自動給定類型,而且型別就算沒定義也不會是 any
因為 TypeScript Compiler 會先找到 iterate 的目標對象 names
,並依照 names
裡的值去自動推斷出 s
的型別
以上這個過程稱為 「contextual typing」,中文直翻為「情境推斷」,也就是說 函式發生的情境決定了參數應該是什麼型別
這跟「type inference 型別推斷」很類似,多了解一點能幫助你在為來更得心應手的判斷何時可以安心的省略「型別註釋」
let f4 = (a: string, b: string): string => {
return a + b;
}
泛型函式就是「泛型」結合「函式」<T>
是個型別代稱,代表「任意的型別」
此範例接受一個型別為 T
的參數並直接回傳,因此,輸入什麼類型,傳回來的就是什麼類型
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>('myString'); // string
let output2 = identity<number>(100); // number
let output3 = identity<number>('100'); // ❌ Argument of type 'string' is not assignable to parameter of type 'number'.
先前都是針對「函式本身」做講解
那完整定義一個變數是「函式型別」會怎麼寫呢?
格式:
(參數1: <type1>, 參數2: <type2>...) => <回傳的type>
下方例子中的第一個箭頭(=>)後
代表「回傳值的型別」
,要注意它並非 ES6 的箭頭函式,別混淆了
可以用等於(=)
將函式拆成左右兩邊來看,比較不會眼花 XD
let f4: (a: string, b: string) => string = function(a: string, b: string): string {
return a + b;
}
let f5: (a: string, b: string) => string = (a: string, b: string): string => {
return a + b;
}
function sayHi(fn: (a: string) => void) {
fn("Hello TypeScript");
}
function printToConsole(s: string) {
console.log(s); // Hello TypeScript
}
sayHi(printToConsole);
❌ 前後定義沒有一致,就會報錯
使用 interface
去定義函式的形狀
interface StrFunc {
(input: string): string;
}
const repeatStr: strFunc = function(input: string): string {
return input.repeat(2);
};
console.log(repeatString('hello')); // hellohello
...
) 碰到函式ES6 的其餘參數(Rest parameters) 能將剩餘的參數透過 ...
符號將其放入一個陣列中,所以在 TypeScript 中我們可以使用 array []
型別來定義...numbers
被轉換為一個元素為數字的陣列
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
console.log(sum(10, 20)); // 30
函式的表示方法很多種,文中分解成不同類型的函式去講解,並把函式拆成「參數」、「回傳值」兩部分來看,「參數」還可以搭配 可選屬性
,喜歡今天的文章的話可以點個 Like 唷 ^^
大家晚安~開完會繼續趕鐵人賽,快閉眼了 XOX
每天講的內容有推到 github 上喔