關於函式,除了前一個章節講到的動詞開頭外,針對參數設計、命名也有相對應的規範,避免有大量不知其所以然的參數,導致開發受到阻礙。
在我們的應用中,會有兩類型的變數,一種是尚未解析過的,另一種是已經解析過的,解析的意思在於是否有轉化成我們所定義的型別。以底下為例:
const res = await axios.get("https://example.com/member/001");
// res 為尚未解析過的變數
const member = {
id: res.data.id,
name: res.data.fullName,
};
// member 為解析過的變數
若是尚未解析的變數,使用 map/forEach 等需要 iterator 的時候會採用 v, w, x, y, z 等無語意的方式來命名,反之遇到已經解析過的變數,則是以該變數的單數型態作為 iterator 來命名。
const res = await axios.get("https://example.com/members");
const members = res.data.map((d) => ({ id: d.id, name: d.fullName })); // res 尚未被解析,故使用 d 作為 iterator
const memberNames = members.map((member) => member.name); // members 已被解析,故使用 member 作為 iterator
在過去開發中,針對函式參數的討論不下好幾次,最後我們花了近三小時把各種情境收斂下來,決定各自退讓一步抓取平衡。一般來說,我們會希望在使用函式的時候可以很清楚的知道哪些參數是需要必帶的,哪些可以省略,因此直覺來看會是以下的設計:
const func = (req1: string, req2: number, opt1?: number, opt2?: boolean) => {};
然而當參數的數量增加時就會變得非常可怕,因此就想說把他們整個打包起來變成:
const func = (params: {
req1: string;
req2: number;
opt1?: number;
opt2?: boolean;
}) => {};
但是這種樣子實際上並不容易開發,我們從 VScode 的提示很容易看錯,也不好看清楚哪些是必填,反而增加了 compile error 的頻率,因此我們調整成了最終的模式:
const func = (
req1: string,
req2: number,
opt?: {
opt1?: number;
opt2?: boolean;
}
) => {
const opt1 = opt?.opt1 || 0; // default value
const opt2 = opt?.opt2 || true; // default value
};
將必填的抽離出來,選填的全部放在 option 的參數中,只要在實作 function 的時候給予預設值(ts 會提醒),那麼開發上就不容易出錯。