我們在前面 Day09 , 有簡單討論到 function,這篇就會來看一些更深入 function 的應用。
最簡單定義 function 的用法:
function greeter(fn: (a: string) => void) {
fn("Hello, World");
}
function printToConsole(s: string) {
console.log(s);
}
greeter(printToConsole); //Hello, World
(a: string) => void
表示這個function帶入的一個命名為 a 的參數,這個參數的型別為字串, 且此 funtion 沒有 return 值。
特別注意一定要有參數命名,如(a: string)
。如果這樣沒命名, 這樣寫 (string) => void
就會表示這個參數命名為string, 型別是 any
。
此外, type 及 interface 也能定義 function , 可參考 Day15 這篇。
其實這個我一開始真的看不懂,感謝google大神讓我找到實現官網例子的方法,大概了解了,下面也來實作一下。
在 JavaScript 中, function 是可以增加屬性的(其實我不知道欸,好像沒有試過對 function 增加屬性)。於是想來證明是否真的可以,寫了小例子,還真的可以增加屬性:
const jsFunc = () => {
return "this's a function";
}
jsFunc.prop = "this's a prop";
jsFunc.desc = "this's a desc";
console.log(jsFunc); //[Function: func] { prop: "this's a prop", desc: "this's a desc" }
但在 TypeScript 中 type expression 是不允許對 function 新增屬性的,於是我們可以在 object type 寫一個 call signatures, 如下 DescribableFunction
。特別注意跟一般 function 不同的是返回型別是使用 :
, 不是使用 =>
。
//新增 call signatures
type DescribableFunction = {
description: string;
(someArg: number): boolean; //這邊特別注意我們使用:非 =>
};
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
//回傳boolean 如果大於5回傳true
const func = (someArg: number): boolean => {
return someArg > 5;
};
//新增屬性
func.description = "isNumber > 5";
doSomething(func); //isNumber > 5 returned true
一般我們使用 new 來建立新 object, 就稱為 constructors 。
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
雖然在 JavaScript 中沒有被帶入的函式參數會是 undefined 且可以正常運作,但在 TypeScript 中,函式的參數預設都是必填的,沒填的話 compiler 會直接噴錯。
跟之前在介紹 object 型別可選屬性一樣,參數也可以選填,在參數後面使用?
。
// firstName 是必填,lastName 則可以選填
const getName = (firstName: string, lastName?: string) => {
return lastName ? `${firstName} ${lastName}` : firstName;
}
⚠️ 特別注意的是optional parameter 一定只能放在 required parameter 的後面。
⚠️ 官網也有特別提醒在 callback 的時候也盡量不要使用 optional parameter, 可能會發生這種錯誤。
const getName = (firstName: string, lastName = "Chen") => {
return `${firstName} ${lastName}`;
}
console.log(getName("Tom")); //Tom Chen
如果要使用預設值,在呼叫該函式的時候要使用 undefined
。
const getName = (firstName = "Tom", lastName: string) => {
return `${firstName} ${lastName}`;
}
console.log(getName(undefined, "Chen")); //Tom Chen
想新增無限組參數,可使用剩餘參數關鍵字 ...
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x);
}
const a = multiply(10, 1, 2, 3, 4);
console.log(a); //[10, 20, 30, 40]
const getName = (firstName: string, ...rest: string[]) => {
return `${firstName} ${rest.join(' ')}`;
}
const names = getName('Tom', 'Jerry', 'Chen');
console.log(names);
雖然可以用 push 的方式新增剩餘參數是沒問題:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]
但在 TypeScript , 但在一些使用上會有問題:args
會被 inferred 型別為 number[]
, 不是特定兩個數字。
// Inferred type is number[] -- "an array with zero or more numbers",
// not specifically two numbers
const args = [8, 5];
// error
const angle = Math.atan2(...args);
Math.atan2 是什麼 可參考這裡。
我們可以把陣列直接當成 Tuple 使用,可以在定義陣列是加上 as const
,如此它就是會是一個值固定的 readonly array,這樣就沒問題了。如果沒加上 as const
的話,則會是 number[]
:
// Inferred as 2-length tuple
const args = [8, 5] as const;
// ok
const angle = Math.atan2(...args);
下一篇來筆記 Function Overloads 函式超載 ,第一次知道有這種功能, 覺得很cool。
https://zhuanlan.zhihu.com/p/266823134
https://stackoverflow.com/questions/66874130/how-to-properly-use-functions-construct-signatures-in-typescript
https://basarat.gitbook.io/typescript/type-system/type-inference
https://pjchender.dev/typescript/ts-functions/#%E9%81%B8%E5%A1%AB%E6%80%A7%E7%9A%84%E5%8F%83%E6%95%B8%E8%88%87%E5%8F%83%E6%95%B8%E9%A0%90%E8%A8%AD%E5%80%BC
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
https://pjchender.dev/typescript/ts-basic-types/