Tips for implementing multiple yield generators in redux-saga

Our redux-saga generator has multiple yield statements that return different results. I am struggling with typing them correctly.

Here's an illustration:

const addBusiness = function* addBusiness(action: AddBusinessActionReturnType): Generator<
  Promise<Business> | SelectEffect | PutEffect<{ payload?: ActionPayload<Array<Business>>; type: string }>,
  void,
  Business | BusinessesContainer
> {
    const { url, showToast = true } = action.payload;

    const businessDetails: Business = yield Network.get<Business>( // TypeScript error: Type 'Business | BusinessesContainer' is not assignable to type 'Business'.
      `businesses?url=${url}`,
    );

    if (showToast) {
      const getBusinessesFromState = (state: AppState) => ({
        ...state.business.businesses,
      });
      const businesses: BusinessesContainer = yield select(getBusinessesFromState); // TypeScript error: Type 'Business | BusinessesContainer' is not assignable to type 'BusinessesContainer'
      onAddBusinessSuccessToast(businesses, businessDetails);
    }

    yield put({ // TypeScript error: Type 'SimpleEffect<"PUT", PutEffectDescriptor<{ type: string; payload: Business[]; }>>' is not assignable to type 'SelectEffect'
      type: constants.SAVE_BUSINESS_REQUEST,
      payload: [businessDetails],
    });

The comments above indicate the TypeScript errors we are encountering. Any assistance would be greatly appreciated. Thank you

Answer №1

It appears that there may be a tendency to type too much in this particular case. It is true that

yield Network.get<Business>()
does indeed return a Business, so there should not be a need to declare const businessDetails: Business when assigning a Business value to it.

Dealing with generator return types can be challenging, but TypeScript is capable of inferring the correct type on its own most of the time. Omitting the return type altogether would still work fine. If you're struggling with determining the right type, you can temporarily remove the return type to see the inferred type, as I did to address the issue of changing the union Business | BusinessesContainer to an intersection

Business & BusinessesContainer
.

The necessity of the ActionPayload type is unclear. Typing the payload of your put action as

ActionPayload<Array<Business>>
is overshooting, as Array<Business> alone suffices. Making the payload optional when you always provide it serves no purpose.

Implementing those two adjustments should resolve your TypeScript issues for now. Typically, API calls are executed within a call effect, which could complicate the types further, although it's ambiguous whether the use of call is mandatory.

const addBusiness = function* addBusiness(action: AddBusinessActionReturnType): Generator<
  Promise<Business> | SelectEffect | PutEffect<{ payload: Array<Business>; type: string }>,
  void,
  Business & BusinessesContainer
> {
    const { url, showToast = true } = action.payload;

    const businessDetails: Business = yield Network.get<Business>( `businesses?url=${url}` );

    if (showToast) {
      const getBusinessesFromState = (state: AppState) => ({
        ...state.business.businesses,
      });
      const businesses: BusinessesContainer = yield select(getBusinessesFromState);
      onAddBusinessSuccessToast(businesses, businessDetails);
    }

    yield put({
      type: "SAVE_BUSINESS_REQUEST",
      payload: [businessDetails],
    });
  }

// dummy types that I filled in to check types

type Business = {
  name: string;
  id: number;
}

type BusinessesContainer = {
  businesses: Business[];
}

const Network = {
  get: async <T>(url: string): Promise<T> => {
    return {} as unknown as T;
  }
}

type AddBusinessActionReturnType = {
  type: string;
  payload: {
    showToast?: boolean;
    url: string;
  }
}

type AppState = {
  business: {
    businesses: BusinessesContainer;
  }
}

const onAddBusinessSuccessToast = (businesses: BusinessesContainer, businessDetails: Business) => undefined;

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

NativeScript Error Code NG8001: Element 'ActionBar' is unrecognized

In my project, the startupscreen module setup is as follows: import { NativeScriptFormsModule } from "@nativescript/angular"; import { NativeScriptCommonModule } from "@nativescript/angular/common"; import { NgModule, NO_ERRORS_SCHEMA } ...

Using Promise<void> instead of Promise<any> is the preferred approach

Working with AngularJS, I have created several asynchronous functions that all use the same signature, which is app.Domain.GenericModel.EntityBase (my generic model). Here is an example: get(resource: string): ng.IPromise<app.Domain.GenericModel.Entity ...

Vue3 and Typescript issue: The property '$el' is not recognized on type 'void'. What is the correct way to access every existing tag type in the DOM?

Currently, I am in the process of migrating a Vue2 project to Vue3 and Typescript. While encountering several unusual errors, one particular issue with $el has me puzzled. I have been attempting to target every <g> tag within the provided template, ...

Using React to make an API call without utilizing hooks

Hello, I am currently working on developing a webpart using SharePoint and React. However, I am facing some issues with fetching data from a simple API. export default class Testing100 extends React.Component<ITesting100Props, {}> { constructor(p ...

Error Alert: "Invariant Violation" detected in a TypeScript program utilizing React

When attempting to implement the react-collapse component in my TypeScript application along with a custom d.ts file, I encountered the following error: Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a ...

Testing the number of times module functions are called using Jest

As someone who is relatively new to JavaScript and Jest, I am faced with a particular challenge in my testing. jest.mock('./db' , ()=>{ saveProduct: (product)=>{ //someLogic return }, updateProduct: (product)=>{ ...

Issue with accessing data in React Admin Show Page using useRecordContext() function leads to undefined return

Within a basic RA application, I am attempting to showcase an item known as a "card" utilizing a Show Page. The fields—specifically id and title—are being presented correctly. Nevertheless, the useRecordContext() hook is consistently returning undefin ...

Can the month dropdown in react-datepicker be modified to display numbers instead of names?

Here is the code I have: <DatePicker selected={value?.toDate()} onChange={(date) => this.handleMonthChange(date)} inline showMonthYearPicker dateFormat={this.props.formatString} /> https://i.sstatic.net/SkdGP.png I am seeking ...

Importing dynamically into Ionic 2 from locations other than the "node_modules" directory

I've recently reviewed the documentation for ModuleResolution in TypeScript on this page: https://www.typescriptlang.org/docs/handbook/module-resolution.html#node My understanding is that all files I wish to import must reside within the node_modules ...

What is the process for extracting TypeScript types from GraphQL query and mutation fields in order to get args?

I am experiencing difficulties with utilizing TypeScript and GraphQL. I am struggling to ensure that everything is properly typed. How can I achieve typed args and parent properties in Root query and mutation fields? For instance: Server: export interfa ...

What is the most effective method of testing with jest to verify that a TypeScript Enum list contains all the expected string values?

Recently, I created a list of enums: export enum Hobbies { Paint = 'PAINT', Run = 'RUN', Bike = 'BIKE', Dance = 'DANCE' } My goal is to iterate through this list using Jest and verify that all the string ...

Deactivating Google Map Clustering for Individual Markers

I'm currently utilizing Angular 4, Google Maps v3, and Marker Clusterer v2 - all of which are the latest versions. I'm attempting to implement a straightforward example found in the official Google Maps documentation (https://developers.google.co ...

The parameter type 'typeof LogoAvatar' cannot be assigned to the argument type 'ComponentType<LogoProps & Partial<WithTheme>'

Why is the argument of type typeof LogoAvatar not assignable to a parameter of type ComponentType<LogoProps & Partial<WithTheme>? Here is the code snippet: import * as React from "react"; import { withStyles } from "@material-ui/core/style ...

Can you determine if a function is a generator function if .bind() has already been applied to it?

It seems like using .bind(this) on a generator function is interfering with determining if the function is actually a generator. Any suggestions on how to resolve this issue? function checkIfGenerator(fn) { if(!fn) { return false; } ...

Exploring the intricacies of extracting nested JSON data in TypeScript

Can someone help me with this issue? https://example.com/2KFsR.png When I try to access addons, I only see [] but the web console indicates that addons are present. This is my JSON structure: https://example.com/5NGeD.png I attempted to use this code: ...

Issue with redirecting to another link in Angular routing

After numerous attempts, I finally managed to configure the adviceRouterModule correctly. Despite extensive research and Google searches, I couldn't quite crack it. Here is the configuration for my AdviceRoutingModule: const adviceRouters: Routes = ...

Display different icons in an Angular application based on the value received from an API in real

My goal was to create a dynamic form that displays icons for the fields I have created. Here is a snapshot of my UI screen showing the field explorer with the list coming from an API. https://i.sstatic.net/4Ye9G.png I need to place an icon in front of ea ...

Adjusting the settimeout delay time during its execution

Is there a way to adjust the setTimeout delay time while it is already running? I tried using debounceTime() as an alternative, but I would like to modify the existing delay time instead of creating a new one. In the code snippet provided, the delay is se ...

ESLint encountered an issue: Reserved keyword 'interface' triggered a parsing error

Whenever I utilize the most recent version of eslint to initiate a project, a specific error pops up: import { ref } from 'vue' defineProps<{msg: string}>() const count = ref(0) Error message: Unexpected token )eslint Adjusting the code ...

Retrieve a collection of nested dictionaries containing flask and angular data

In my app development journey with flask and ionic(angular), I'm working on returning a JSON list. Here's the python code snippet: def get-stocks(): # Select all stocks cursor.execute("""SELECT * FROM `tbl_symbol_index`"" ...