iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 26
1
Software Development

為什麼世界需要Typescript系列 第 26

動手做柯里化 - 26

  • 分享至 

  • xImage
  •  

首先我們開啟VSCode , 按照下面動手做.

繼續回到 add 方法, 一般的 add 加法函數有兩個輸入參數回傳值是數字, 我們先宣告如下

function add(a: number, b: number): number;

之前上面有說明柯里化是...

柯里化後的新的函數, 輸入只有一個參數, 回傳值也是函數

然後我們要將 add 柯里化, 所以再加以下宣告

function add(a: number): (b: number) => number;

(b: number) => number 是一個函數宣告, 表明有一個輸入參數 b, 回傳值是 number

然後我們建立真正的add 加法函數, 輸入參數是無限個, 裡面不考慮加法如何實做, 我們先故意回傳 null

function add(...args: any[]): any {
   return null as any;
}

整個程式碼應該如下

function add(a: number, b: number): number;
function add(a: number): (b: number) => number;
function add(...args: any[]): any {
   return null as any;
}

這是Typescript 的多載函數宣告方法.

函數多載 是程式語言中允許建立很多個函數名稱相同, 但輸入參數類型可以不同, 數量也能不同, 甚至回傳值類型也可以不同

現在我們輸入

type OverloadedAdd = typeof add;

我們用滑鼠移到 OverloadedAdd 上, 你會看到Typescript 描述 add 的柯里化函數的內容, 如下所示

type OverloadedAdd = {
   (a: number, b:number): number;
   (a: number): (b: number) => number;
};

現在我們知道Typescript 對於 add 函數的柯里化宣告定義是甚麼樣子了.

清除所有程式碼, 這次我們輸入 CurryAdd 柯里化

type CurryAdd = {
   (a: number, b:number): number;
   (a: number): (b: number) => number;
};

然後我們要重構一下, 提取這個部分

(b: number) => number 

變成下面程式碼

type CurryAdd1 = (b: number) => number;

因為只有一個參數, b 名稱很難看, 我們修改名稱為 a

type CurryAdd1 = (b: number) => number;

最後上述程式碼重構完後, 完整程式碼如下

type CurryAdd1 = (a: number) => number;

type CurryAdd2 = {
   (a: number, b:number): number;
   (a: number): CurryAdd1;
};

觀察上面的程式碼, 你可以發現 type CurryAdd2 第一行是帶有兩個參數的原始宣告, 第二行是柯里化宣告.

故假如我們要 CurryAdd2 加法函數能夠接受三個參數.

所以我們可以這樣做, 建立 CurryAdd3 第一行表示要三個參數, 然後後面加上兩個 Curry 柯里化函數

type CurryAdd3 = {
   (a: number, b:number, c:number): number;
   (a: number, b:number): CurryAdd1;
   (a: number): CurryAdd2;
};

到目前為止, 我們可以手動建立輸入 n 個參數的 Curry 柯里化函數.

現在思維跳回一般的物件導向語言, 實際上我們的一般物件函數的定義, 並非只有兩個參數, 或三個參數. 如果我們手動輸入把這些物件導向函數柯里化為新的函數.

遇到物件導向函數有 5 個輸入參數, 那我們就得手工敲很多程式碼才能夠將它柯里化....

所以我們必須有一種方法可以重複做到這樣的功能.

幸好Typescript 有提供 "條件類型" (Conditional Types), 它允許我們根據條件判斷, 而回傳類型.

以下是Typescript 條件類型判斷語法

type ConditionalType<T> =
   T extends SomeType ? TypeA : TypeB;

然後當我們輸入以下程式碼, Typescript 就會判斷 TypeX 的類型是不是 SomeType , 如果是的話就回傳 TypeA, 不是的話就回傳 TypeB .

const x: ConditionalType<TypeX>;

所以在我們的 add 加法柯里化例子, 我們可以定義新的 CurryAddX 的條件類型

type CurryAddX<T> =
   T extends [number, number, number] ? CurryAdd3 :
   T extends [number, number] ? CurryAdd2 :
   T extends [number] ? CurryAdd1 :
   unknown
;

這個 CurryAddX 條件類型檢查 T 是否有匹配 [number, number, number] 三個參數, 有的話就給 CurryAdd3 柯里化函數.

沒有的話又檢查 T 是否有匹配 [number, number] 兩個參數, 有的話就給 CurryAdd2 柯里化函數.

以此類推到 unknown 未知結果.

unknown 是 Typescript 提供的類型, 表示是未知類型. 但是更安全的作法是動態檢查它的類型.


上一篇
什麼是柯里化 - 25
下一篇
泛型柯里化 - 27
系列文
為什麼世界需要Typescript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言