javascript - 包装函数的类 : get correct typings and access to number of arguments (in typescript)

标签 javascript typescript wrapper

我写了一个小类,它包装了一个指定的函数,还为每个函数参数获取了一个验证模式列表。这个类有一个 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 )。所以不可能构造带有错误验证的类实例。
  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] )。但这应该还是可以接受的。
  • 但是,我们无法检查验证模式的数量,因为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()取决于构造函数重载。

我没有在 typescript 中重载的经验,但我猜具有独立重载函数的代码示例可能看起来像这样:

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.

关于javascript - 包装函数的类 : get correct typings and access to number of arguments (in typescript),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50062217/

相关文章:

angular - 执行所有for循环后执行代码

Java 包装器内存不足

javascript - 为命名空间上公开的方法创建类型定义

javascript - 硫化破坏了 polymer 代码(未捕获的类型错误 : undefined is not a function)

javascript - 在函数中从派生类访问基成员

c# - C#中wrapper的自动转换

c++ - 如何使用 C++/CLI 包装器库为 native C++ 库导出多个类

javascript - 如何在wix后端获取/连接数据库字段值

javascript - 基于属性将javascript中的对象数组拆分为单独的数组

reactjs - 在 React Typescript 中遇到 No overload matches this call 的错误