打字稿:包装函数返回泛型类的类型

时间:2021-07-30 17:49:50

标签: typescript mocking typescript-typings typescript-generics

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

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

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

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

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

2 个答案:

答案 0 :(得分:3)

刚刚通过使用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

答案 1 :(得分: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;
    }
};