簡體   English   中英

基於輸入參數的Typescript函數重載

[英]Typescript function overloading based on input arguments

我正在尋找一種重寫或重載特定功能組的方法。

這是一個例子

const operationA = ({a, b, c}) => 'alpha';
const operationB = ({a, b }) => 'beta';
const operationC = ({a}) => 'gamma';

const operation = compose([operationA, operationB, operationC]);

operation({a}) // 'gamma'
operation({a, b}) // 'beta'
operation({a, b, c}) // 'alpha'

有沒有辦法在打字稿中執行此功能?

(下面我使用TypeScript 3.2)

如果我理解,您的問題的主要問題是在運行時選擇正確的重載的難度。 將類型信息從TypeScript編譯為JavaScript 並不是TypeScript的目標之一 (請參閱非目標5)。 TypeScript添加的類型系統在運行時會被完全擦除。 因此,如果要編寫compose()來獲取函數列表,則必須以某種方式能夠在運行時檢查這些函數以確定應在特定參數上調用哪個函數。 但是,該功能實際上在JavaScript中不存在。 好的,您可以使用一個函數的length屬性來查看它期望有多少個參數,但是在您給出的示例中,每個函數僅使用一個參數。 因此,我們不能在此處使用該方法。

一種可能的前進方式是向每個函數添加屬性 此屬性將是一種方法,它使用一組可能的參數,如果這些參數對該函數有效,則返回true否則返回false 本質上,您是在手動添加該語言所缺少的必要檢查功能。

如果這樣做,我們可以使compose()接受此類“參數驗證函數”的列表,如下所示:

type ArgValidatingFunction =
  ((...args: any[]) => any) & { validArgs(...args: any): boolean };

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends
  ((k: infer I) => void) ? I : never;

function compose<F extends ArgValidatingFunction[]>(...fn: F): UnionToIntersection<F[number]>;
function compose(...fn: ArgValidatingFunction[]): Function {
  return Object.assign(
    (...args: any[]) => (fn.find(f => f.validArgs(...args))!(...args)),
    { validArgs: (...args: any[]) => fn.some(f => f.validArgs(...args)) }
  );
}

compose的類型簽名接受ArgValidatingFunction參數列表,並返回其元素交集 TypeScript將重載表示為簽名的順序相關交集。 我不能100%保證編譯器會產生與傳遞的函數相同的重載順序,但似乎可以在我的測試中使用。

compose實現利用ArgValidatingFunctionvalidArgs方法,並對傳入的函數執行find()以選擇適當的函數。 我還在返回的函數上實現了validArgs()方法,以便compose()的返回值也是ArgValidatingFunction (這很不錯,因為類型簽名聲稱是)。

現在我們可以嘗試使用它,但是它並不簡單……我們必須添加這些方法:

const operationA = ({ a, b, c }: { a: any, b: any, c: any }): 'alpha' => 'alpha';
operationA.validArgs = (...args: any[]) => 
  (args.length === 1) && ('a' in args[0]) && ('b' in args[0]) && ('c' in args[0]);

const operationB = ({ a, b }: { a: any, b: any }): 'beta' => 'beta';
operationB.validArgs = (...args: any[]) => 
  (args.length === 1) && ('a' in args[0]) && ('b' in args[0]);

const operationC = ({ a }: { a: any }): 'gamma' => 'gamma';
operationC.validArgs = (...args: any[]) => 
  (args.length === 1) && ('a' in args[0]);

開始了:

const operation = compose(operationA, operationB, operationC);

const beta = operation({ a: 3, b: 3 }); // "beta" at compile time;
console.log(beta); // "beta" at runtime

看起來它在編譯時和運行時都可以工作。


所以這是要走的路。 這並不容易,也不是很漂亮,但是也許適用於您(或某人)的用例。 希望能有所幫助。 祝好運!

您可能已經評估過的一種方法是,將Interface用作主要operation方法的輸入,然后根據輸入分配正確的子方法。

所以像這樣:

interface OperationArgs {
 a: string;
 b?: string;
 c?: string;
}

因此,第一個值是必需的,其他兩個是可選的。

在您的operation方法內,您可以執行以下operation

public operation(inp: OperationArgs) {
  if (inp.c) {
    return this.operationC(inp);
  }
  if (inp.b) {
    return this.operationB(inp);
  }
  return this.operationA(inp);
}

另一種方法是使用代理,但是在JS中尚未完全支持它們(缺少瀏覽器)。 您可以創建一個返回Proxy實例的類,並使用處理程序的get方法捕獲operation方法。 根據給定的道具,您實際上將在實例上調用正確的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM