Determining the presence of generic K within generic M in Typescript Generics and Redux

Hello there

I am currently working on minimizing repetitive code in my react application by utilizing Redux state. After choosing the Redux structure to use (refer to Context), I now aim to make it more concise. To achieve this, I have developed a generic actionCreator. However, I am facing difficulty in determining the desired generic syntax.

Generic Action Creator

Here is my approach towards creating a generic action generator:

export class genericAction<M, K> extends Action {
    public reducer = (state: M) => ({ ...state, ...this.payload });

    constructor(public readonly type: string | undefined, public payload: K) {
        super();
    }
}

Currently, only merging states is possible, but I am aiming for an optional parameter that includes a function. This function will enable custom state merges (e.g. state.count + payload). However, this is a separate topic of discussion.

Issue at Hand

The problem arises when I consider using { fubar: number} instead of { loading: boolean }. Clearly, 'fubar' does not belong in my ProductStateModel.

const someAction = new genericAction<ProductStateModel, { loading: boolean }>(ProductListActionTypes.REQUEST_START, { loading: true });

Inquiry

I wish to only utilize attributes from the ProductStateModel as the type for the generic K. Pseudo Code:

genericAction<M, K is type of M> extends Action

The overall goal is to generate the action with the following types and parameters:

  • StateModel
  • ProductListActionType
  • Payload

Is this achievable? Or should I be considering a different solution altogether? I am open to modifying the StateModel if necessary. As someone new to redux, I strive to follow best practices. I attempted to implement Pick<T, K> in the generic method, but I am uncertain whether it aligns with my requirements or maybe I simply need some rest :D

Appreciate any guidance and assistance


Context

This marks my starting point

// State
export interface State {}

export interface ProductStateModel extends State {
    products: Array<ProductDTO>;
    loading: boolean;
    error: string;
}


// Product Actions
export abstract class Action {
    public readonly type: string | undefined;
    protected constructor() {
        return Object.assign({}, this);
    }
    abstract reducer(state: State): State;
}

// First of 3 Actions. They are all very similar. The generic function should replace all of them.
export class ProductListRequest extends Action {
    public readonly type = ProductListActionTypes.REQUEST_START;
    public reducer = (state: ProductStateModel) => ({ ...state, loading: this.payload.loading });

    constructor(public payload: Pick<ProductStateModel, 'loading'>) {
        super();
    }
}

// Reducer
export const productListReducer = (state: ProductStateModel = defaultProductState, action: Action) => {
    return isNil(action.reducer) ? action.reducer(state) : state;
};

Answer №1

class CustomAction<T> extends Action {
    public updater = (data: T) => ({ ...data, ...this.update });

    constructor(public readonly actionType: string | undefined, public update: Partial<T>) {
        super();
    }
}

I decided to modify the generic class by removing the second type declaration and introducing Partial to the payload generic type. Initially, I wanted to restrict the allowed attributes as done in the original approach using

Pick<ProductStateModel, 'loading'>
. However, upon reflection, I realized this was unnecessary. The instantiation of the generic class now determines which attributes are necessary for the action, simplifying the process.

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

Is it necessary for me to develop an Angular library in order to release a basic TypeScript class that makes use of Angular components?

In one of my Angular projects, I have a Typescript class called APIResponse that I want to convert into an NPM package for use in other projects. This class is not specific to Angular like a Service or Component. After doing some research on creating non-A ...

I'm debating on where to dispatch my redux

Currently, I am delving into the world of React and Redux, and a question has been nagging at me. Should I dispatch and share my Redux store in individual components that need it, or would it be better to centralize it in one main component and pass down t ...

Tips for retrieving items from <ng-template>:

When the loader is set to false, I am trying to access an element by ID that is located inside the <ng-template>. In the subscribe function, after the loader changes to false and my content is rendered, I attempt to access the 'gif-html' el ...

What are the best strategies for combining multiple TypeScript class decorators?

I have a set of unique class decorators that I apply to multiple classes. It looks something like this: @awesome @cool @amazing export class MySpecialClass { /* ..... */ } However, since I use these three decorators across many classes, I want to condens ...

The Application Insights Javascript trackException function is giving an error message that states: "Method Failed: 'Unknown'"

I've been testing out AppInsights and implementing the code below: (Encountering a 500 error on fetch) private callMethod(): void { this._callMethodInternal(); } private async _callMethodInternal(): Promise<void> { try { await fetch("h ...

Nest may struggle with resolving dependencies at times, but rest assured they are indeed present

I've encountered a strange issue. Nest is flagging a missing dependency in a service, but only when that service is Injected by multiple other services. cleaning.module.ts @Module({ imports: [ //Just a few repos ], providers: [ ServicesService, ...

How to effectively manage radio buttons in Angular 6

Here are some questions I have listed below. public QandAList = [ { question:{ id: "Q1", query:"What is the capital of France?" }, options:[ { id: "opt1", text: "Paris" }, ...

Endpoint path for reverse matching in Mongodb API

I am currently in the process of setting up a webhook system that allows users to connect to any method on my express server by specifying a method and an endpoint to listen to (for example PUT /movies/*). This setup will then send the updated movie to the ...

The state of dynamically created Angular components is not being preserved

My current task involves dynamically creating multiple components to be placed in a table. The code successfully achieves this objective, but the state seems to be getting jumbled up at the level of the dynamically generated components. When a component is ...

The object assigned in the Facebook login method cannot be utilized

I'm working on integrating Facebook login with Angular 2. Here's the button for logging in: <button ion-button (click)="fbLogin()"><ion-icon name="logo-facebook"></ion-icon>Login using Facebook</button> Below is the clic ...

Discovering a solution to extract a value from an Array of objects without explicitly referencing the key has proven to be quite challenging, as my extensive online research has failed to yield any similar or closely related problems

So I had this specific constant value const uniqueObjArr = [ { asdfgfjhjkl:"example 123" }, { qwertyuiop:"example 456" }, { zxcvbnmqwerty:"example 678" }, ] I aim to retrieve the ...

Issue with mui TextField label width not adjusting properly with font override

Whenever I change the font of the label, the width of the label does not adjust accordingly and the text appears to be outlined. For a demonstration, you can check out this example on CodeSandbox ...

Angular2 Cascading Animations

I have been working on implementing cascaded animations for a list of elements. Although I successfully applied the state triggers, following the guidelines in the documentation, I am encountering an issue where all element states are being applied simult ...

Is there a way to dynamically retrieve a JSON element in Typescript?

I am using a data grid throughout my application, and currently, I am retrieving the selected rowid with the following code. Here is the HTML snippet: <tbody> <tr *ngFor="let ddata of tableData.data; let i = index" (click)="setClickedRow(ddat ...

What is the best way to call a method from app.component in another component?

Having recently delved into Typescript and Angular 2, I've been struggling to find a solution online that fits my needs. Let's consider the example of an app.component: export class AppComponent implements OnInit { constructor(public _test ...

Best Practices for Implementing Redux Prop Types in Typescript React Components to Eliminate TypeScript Warnings

Suppose you have a React component: interface Chat { someId: string; } export const Chat = (props: Chat) => {} and someId is defined in your mapStateToProps: function mapStateToProps(state: State) { return { someId: state.someId || '' ...

Utilizing React-Input-Mask (written in TypeScript) to conceal Material UI input within Formik forms

Issue with Formik Input and TextField Type Error <InputMask mask="99/99/9999" value={formik.values.phone} onChange={formik.handleChange} onBlur={formik.handleBlur} > {(inputProps: Pro ...

What is the method for locating an element within an array?

The content being returned is presenting a challenge. How can I retrieve data from inside 0? I attempted to access it using date[0] without success const { data } = getData(); The result of console.log(data) is shown below: enter image description here ...

Unable to access property value following AJAX call

Here is my code snippet: constructor(props: any) { super(props); this.state = { list: [], }; } public componentWillMount() { this.loadData(); } public loadData = () => { axios.get(someURL) .then((response) = ...

Refactoring TypeScript components in Angular

How can I streamline the TypeScript in this component to avoid repeating code for each coverage line? This angular component utilizes an ngFor in the HTML template, displaying a different "GroupsView" based on the context. <div *ngFor="let benefitG ...