Utilizing Typescript to Encapsulate the Return Type of a Generic Class

I can't seem to understand this problem in a general way using Typescript, any assistance would be greatly appreciated!

  • A Factory needs to deploy a Contract
  • A CustomFactory is a type of Factory and should deploy a CustomContract (which is also a Contract)
  • A MockFactory should encapsulate all this logic

The objective is to achieve something like the following (semi pseudocode)

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

To further clarify the issue, I have created a Playground where you can view the errors

Answer №1

Successfully resolved the issue by utilizing ReturnType and Parameters, along with transforming the interface into a type:

interface Agreement {}

interface Device {
    activate: (...args: any[]) => Agreement;
}

class CustomAgreement implements Agreement {}

class CustomDevice implements Device {
    activate(code: number, id: number): CustomAgreement {
        return {};
    }
}

type MockAgreement<A extends Agreement> = Agreement & A & {
    mockDetail: number;
}

type MockDevice<D extends Device> = D & {
    // activate should possess the parameters of the function activate within D (in this case CustomDevice)
    // activate should yield a MockAgreement of the return type of the function activate within D (MockAgreement<CustomAgreement> in a generic manner)
    activate: (...args: Parameters<D['activate']>) => MockAgreement<ReturnType<D['activate']>>
}

const illustration: MockDevice<CustomDevice> = {} as any;
illustration.activate(1, 2);

Enhanced Playground Experience

Answer №2

If you're considering between using (abstract) classes or interfaces, both are viable options. However, when it comes to making the factory itself generic, it may not be the best approach. Instead, consider making the contract type generic. I experimented a bit and managed to piece together this solution:

interface Contract { }

interface Factory<A extends any[], C extends Contract> {
    deploy: (...args: A) => C;
}

// Additional types for assistance
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: 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;
    }
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding dropdowns to divs in Angular applications

Currently, I am attempting to integrate a dropdown feature into a div element. The HTML code for the dropdown is generated dynamically within the code. When clicking on the dropdown button, it appears functional but unfortunately, the dropdown itself does ...

Tips for resolving the unmounted component issue in React hooks

Any suggestions on resolving this issue: Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect ...

Using Jest and Supertest for mocking in a Typescript environment

I've been working on a mock test case using Jest in TypeScript, attempting to mock API calls with supertest. However, I'm having trouble retrieving a mocked response when using Axios in the login function. Despite trying to mock the Axios call, I ...

Invoking a function containing an await statement does not pause the execution flow until the corresponding promise is fulfilled

Imagine a situation like this: function process1(): Promise<string> { return new Promise((resolve, reject) => { // do something const response = true; setTimeout(() => { if (response) { resolve("success"); ...

Implementing NgRx state management to track and synchronize array updates

If you have multiple objects to add in ngrx state, how can you ensure they are all captured and kept in sync? For example, what if one user is associated with more than one task? Currently, when all tasks are returned, the store is updated twice. However, ...

TypeScript Jest encountering TypeError: Class extends value that is either undefined, not a constructor, or null

Currently, I am utilizing ts-jest for writing tests in my ts-node project. While the application is functioning smoothly without any TypeScript errors in my IDE (VSCode), encountering an error when attempting to run my test suite. Test suite failed to run ...

Angular : How can a single item be transferred from an array list to another service using Angular services?

How to Transfer a Single List Item to the Cart? I'm working on an Angular web application and I need help with transferring a single item from one service to another service and also displaying it in a different component. While I have successfully i ...

Tips on setting up an npm package for automatic updates

I recently created a package called https://www.npmjs.com/package/nestjs-notifications After running npm install nestjs-notifications --save, I noticed that it installed successfully but saved the version as: "nestjs-notifications": "0.0.10 ...

Unable to connect input with abstract classes at a hierarchy depth of 2 levels or more

When working on my Angular application: If a Component utilizes an Input that is defined in its immediate parent (abstract) class, everything runs smoothly. However, if a Component uses an Input that is declared in a parent class located two levels a ...

How to call a function within a component from another component without encountering the "Cannot read property" error

Having trouble calling a function from one component in another by passing the reference of one to the other. I keep getting a "Cannot read property" error. Below is the code snippet Alert Component import { Component, OnInit, Output } from '@angula ...

Angular2 data binding does not update when properties change

I'm struggling to establish the connection between the fields and the component in order for them to update when the properties change in OnDataUpdate(). The field "OtherValue" successfully binds two ways with the input field, and the "Name" field di ...

Establish a connection to the ActiveMQ broker utilizing STOMP protocol in an Ionic application

I've recently received an Ionic + Capacitor app that is primarily meant to run on the Android platform. My current task is to incorporate communication with a remote ActiveMQ broker into the app. To achieve this, I utilized the STOMP JS library which ...

Error: Model attribute missing in Adonis JS v5 relationship

Recently, I started diving into the Adonis framework (v5) and decided to build a todo list api as part of my learning process. However, I'm facing an issue concerning the relationship between the User and Todo entities. Let me show you the models fo ...

Leveraging TweenMax within an Angular2 development venture

What is the best way to integrate TweenMax into my Angular2 project? I would like to avoid adding the script directly in my HTML and instead import TweenMax using the following syntax: import { TweenMax } from 'gsap'; Please keep in mind that I ...

What could be causing my code to not run after subscribing to the observables?

In my code, I have two methods that return two lists: one for accepted users and the other for favorite users. The first part of my code works well and returns both lists, but in the second part, I need to filter out the accepted users who are also on the ...

Transform a group of objects in Typescript into a new object with a modified structure

Struggling to figure out how to modify the return value of reduce without resorting to clunky type assertions. Take this snippet for example: const list: Array<Record<string, string | number>> = [ { resourceName: "a", usage: ...

Maximizing Component Reusability in React: Utilizing Various Types Across Components

interface DataInfo { name: string; src: string; id: string; price: number; } interface DataInfo2 { title: string; src: string; _id:string item_price: number; } const ItemData = ({ item }: DataInfo | DataInfo2) => { return ( <li ...

Using Vue components in a TypeScript file

I recently started using Vue.js with TypeScript and stumbled upon this GitHub repository. While working on it, I encountered an issue where I received an error while trying to import a Vue Single Component File from TypeScript. My development environment ...

Accurate function calls with multiple parameters in TypeScript

As a beginner in TypeScript and currently exploring its integration with AngularJS, I am facing a particular issue where the compiler is not detecting an error. In Angular, a resource provider typically includes a get() method that returns an instance of ...

Is there a way to programmatically retrieve the 'title' attribute of a route as it updates during navigation?

Scenario and Issue I have set up various routes in my app-routing.module like this: // imports const routes: Routes = [ { path: 'home', title: 'Home Route', component: HomeComponent }, { path: 'other', title: 'Other ...