【问题标题】:Typescript: Wrap function return type of generic classTypescript:包装泛型类的函数返回类型
【发布时间】:2021-10-06 05:59:24
【问题描述】:

我无法使用 Typescript 以通用的方式解决这个问题,我将不胜感激!

  • 工厂应部署合同
  • CustomFactory 是工厂,应部署 CustomContract(即合同)
  • MockFactory 应该是所有这些逻辑的包装器

目标是这样的(半伪代码)

interface MockFactory<F extends Factory> extends F {
    deploy: (...args: Parameters<F.prototype.deploy>) => MockContract<F.prototype.deploy.returnValue>
}

为了更好地说明问题,我创建了一个Playground,您可以在其中看到错误

【问题讨论】:

    标签: typescript mocking typescript-typings typescript-generics


    【解决方案1】:

    刚刚用ReturnType和Parameters解决了,还需要把接口转成类型:

    interface Contract {}
    
    interface Factory {
        deploy: (...args: any[]) => Contract;
    }
    
    class CustomContract implements Contract {}
    
    class CustomFactory implements Factory {
        deploy(x: number, y: number): CustomContract {
            return {};
        }
    }
    
    type MockContract<C extends Contract> = Contract & C & {
        mockProperty: number;
    }
    
    type MockFactory<F extends Factory> = F & {
        // deploy should have the parameters of the function deploy inside F (in this case CustomFactory)
        // deploy should return a MockContract of the return type of the function deploy inside F (MockContract<CustomContract> in a generic way)
        deploy: (...args: Parameters<F['deploy']>) => MockContract<ReturnType<F['deploy']>>
    }
    
    const example: MockFactory<CustomFactory> = {} as any;
    example.deploy(1, 2);
    
    

    Updated Playground

    【讨论】:

    • 请不要忘记在您的答案中分享您的代码。游乐场链接有时不会长久
    【解决方案2】:

    您可以使用(抽象)类,但接口也是可能的。在这种情况下,使工厂本身具有通用性似乎是错误的举动。改为通用合同类型。玩了一会儿,我就可以一起解谜了:

    interface Contract { }
    
    interface Factory<A extends any[], C extends Contract> {
        deploy: (...args: A) => C;
    }
    
    // Some helper types
    type FactoryArgs<F extends Factory<any, any>> = F extends Factory<infer A, any> ? A : never;
    type FactoryContractType<F extends Factory<any, any>> = F extends Factory<any, infer C> ? C : never;
    
    interface FactoryForClass<C extends new (...args: any) => Contract> {
        //deploy: C extends new (...args: infer A) => infer T ? Factory<A, T>['deploy'] : never;
        deploy: Factory<ConstructorParameters<C>, InstanceType<C>>['deploy'];
    }
    
    class CustomContract implements Contract {
        constructor(a: number, b: number) { }
    }
    
    class CustomFactory implements FactoryForClass<typeof CustomContract> {
        deploy(x: number, y: number): CustomContract {
            return new CustomContract(x, y);
        }
    }
    
    type MockContract<C extends Contract> = Contract & C & {
        mockProperty: number;
    }
    
    type MockFactory<F extends Factory<any, any>> = F & {
        deploy: (...args: FactoryArgs<F>) => MockContract<FactoryContractType<F>>;
    }
    
    const mockFactory: MockFactory<CustomFactory> = {
        deploy(a: number, b: number) {
            const customContract = new CustomContract(a, b);
            const result: CustomContract & MockContract<CustomContract> = customContract as any;
            result.mockProperty = 123;
            return result;
        }
    };
    

    【讨论】:

    • 感谢开尔文的回答!这是一个很棒且更清洁的解决方案。问题是我无法修改合同、工厂、CustomContract 或 CustomFactory。这是因为我正在构建一个包裹它们的mocking library
    • @0xGorilla 这确实使事情复杂化了。您应该仍然可以使用我的type MockFactory&lt;...&gt; = ...,因为它绕过了您原来的MockFactory 界面中的“类型循环”。基本上,您自己发布的答案。
    • @0xGorilla 即使答案不完整但很有帮助,请不要忘记投票 - 就像我一样。它可能会帮助其他人。
    猜你喜欢
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 2020-01-23
    • 2021-05-26
    • 2020-06-09
    • 2021-03-24
    相关资源
    最近更新 更多