簡體   English   中英

包裝函數的類:獲取正確的類型和訪問參數的數量(在typescript中)

[英]Class wrapping a function: get correct typings and access to number of arguments (in typescript)

我編寫了一個包含指定函數的小類,並為每個函數參數提供了一個驗證模式列表。 這個類有一個call函數,它應該使用與輸入函數相同的參數,驗證它們,調用輸入函數,並將結果包裝到rxjs / Observable中。 因此,構造函數應檢查函數method.length指定的參數數量是否與驗證模式的數量相同。

原始的javascript版本應該如下所示:

class Wrapper {
    method;
    validationPatterns;

    constructor(method, validationPatterns) {
        if (method.length !== validationPatterns.length) {
            // throw error
        }
        this.method = method;
        this.validationPatterns = validationPatterns;
    }

    validate(...args) {
        this.validationPatterns.forEach(pattern => {
            // apply validation pattern
        });
    }

    call(...args) {
        // validate arguments
        this.validate(...args);
        // run method and wrap its result e.g. in an Observable
        const methodResult = this.method(...args);
        return Observable.of(methodResult);
    }
}

現在我搜索了很多東西並嘗試了不同的東西來將正確的類型應用到這個類。 但我找不到一種可以讓我這樣做的方法

  1. 訪問構造函數中的函數參數數量(通過method.length )。 因此,無法使用false驗證構造類實例。
  2. 同時對所有類功能都有正確的輸入。

我嘗試了以下事項:

1.使用<T extends Function>

優點:

  • 我可以使用正確的類型訪問類和類實例中的指定方法。

缺點:

  • 沒有辦法從T提取參數類型和返回類型,並將它們應用於validate()call() 他們仍然必須使用any / any[]類型。

代碼示例:

class Wrapper<F extends Function> {

    constructor(public method: F, public validationPatterns: any[]) {
        if (method.length !== validationPatterns.length) {
            // throw error
        }
    }

    validate(...args: any[]): void {
        // ...
    }

    call(...args: any[]): any {
        // ...
    }
}

2.使用單個函數參數。

優點:

  • 這樣,就可以將正確的類型應用於所有功能。

缺點:

  • 需要多個參數的函數需要將它們包裝在正確類型的元組或對象中。 在編寫和調用方法時[a, b]這需要額外的語法(例如括號[a, b] )。 但這應該是可以接受的。
  • 但是,我們無法檢查驗證模式的數量,因為method.length == 1 因此,為了完整打字,我們必須承認功能缺點,這對我來說聽起來不太好。

代碼示例:

class Wrapper<T, U> {
    constructor(public method: (arg: T) => U, public validationPatterns: any[]) {
        // CANNOT check correct number of validation patterns
    }

    validate(args: T): void {
        // ...
    }

    call(args: T): Observable<U> {
        // ...
    }
}

3.超載

由於包裝函數通常只有一些參數,在另一種方法中,我們可以嘗試使用函數重載。 但是,到目前為止,我只找到了有關重載單個函數的信息。 我想,我真正想要的是一個重載類,其中call()validate()的簽名取決於構造函數重載。

我沒有使用打字稿進行重載的經驗,但我想具有獨立重載函數的代碼示例可能如下所示:

class Wrapper<F extends Function, T1, T2, T3, U> {

    constructor(method: (arg1: T1, arg2: T2, arg3: T3) => U, validationPatterns: any[]);
    constructor(method: (arg1: T1, arg2: T2) => U, validationPatterns: any[]);
    constructor(method: (arg1: T1) => U, validationPatterns: any[]);
    constructor(method: () => U, validationPatterns: any[]);
    constructor(public method: F, public validationPatterns: any[]) {
        // ...
    }

    validate(arg1: T1, arg2: T2, arg3: T3): void;
    validate(arg1: T1, arg2: T2): void;
    validate(arg1: T1): void;
    validate(): void;
    validate(...args: any[]): void {
        // ...
    }

    call(arg1: T1, arg2: T2, arg3: T3): Observable<U>;
    call(arg1: T1, arg2: T2): Observable<U>;
    call(arg1: T1): Observable<U>;
    call(): Observable<U>;
    call(...args: any[]): Observable<U> {
        // ...
    }
}

問題

  • 有沒有辦法同時獲得:完全輸入和訪問函數的參數數量?
  • 是否有可能按我想要的方式使用重載?
  • 你建議我去哪種方法? 我目前使用第一個( <T extends Function> ),它具有完整功能但缺少類型。

謝謝!

你可以重載類,但它有點復雜,你需要單獨聲明重載並為每個重載定義一個構造函數:

class WrapperImpl {

    constructor(public method: Function, public validationPatterns?: Function[]) {
        // ...
    }
    validate(...args: any[]): void {
        // ...
    }

    call(...args: any[]): Observable<any> {
        return new Observable<any>();
    }
}


type KeysOfUnion<T> = T extends any ? keyof T: never;
type IsValid<T, TResult> = KeysOfUnion<T> extends never ?  never : TResult;
const Wrapper: {
    new <U>  (m: ()=> U, ) :  {
        validate(): void
        call(): Observable<U>
    }
    new <T1, U>  (m: (a: T1)=> U, validationPatterns: [IsValid<T1, (a: T1)=>boolean>]) :  {
        validate(a: T1): void
        call(a: T1): Observable<U>
    }
    new <T2, T1, U>  (m: (a: T1, a2: T2)=> U, validationPatterns: [IsValid<T1, (a: T1)=>boolean>, IsValid<T2, (a: T2)=>boolean>]) :  {
        validate(a: T1, a2: T2): void
        call(a: T1, a2: T2): Observable<U>
    }
    // Add more as needed 
} = WrapperImpl

let w = new Wrapper(() => "");
w.call() // return Observable<string>

let w2 = new Wrapper((n: number) => "", [n=> true]);
w2.call(10) // return Observable<string>

在實現中我不會過多地使用泛型,你需要使用非常通用的類型(即Functionany )泛型對你沒有多大幫助。 調用將被正確輸入和檢查,這是重要的部分。

我假設驗證是函數,你也可以得到推理和類型安全。 我不得不使用一些條件類型魔法來讓編譯器為每個參數數量選擇正確的重載,但它似乎運行良好。 如果您有任何問題,請告訴我。

當與具有可選參數的函數一起使用時,這種方法仍然至少有一個缺點,因為需要可選參數:

function withOpt(n?: number) { return ""}
let w3 = new Wrapper(withOpt, [n=> true]);
w3.call(1) // 1 is required.

暫無
暫無

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

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