BernardA
BernardA

Reputation: 1523

React Redux Saga with Typescript error property does not exist

Setting up a React, Next js app with TS for the first time and getting a beating at it.

TS throws below error on redux-saga set up:

Property 'name' does not exist on type 'Input | DataPostGreeting | ErrorPostGreeting'. Property 'name' does not exist on type 'DataPostGreeting'

It's accurate that name does not exist on type DataPostGreeting, which is the type of the successful response, but it does exist in Input

The action in question is a POST to create a greeting on a test app.

I thought I had properly typed the payload as Input which is the name of the greeting, but TS does not seem happy with that. Even though it does recognizes Input as part of the applicable types.

This is the saga with the error trigger highlighted:

/store/sagas/greetings.ts

import { all, call, put, takeLatest } from 'redux-saga/effects';
import { actionPostGreetingOk, actionPostGreetingError } from '../actions/creators';
import { Actions } from '../actions/names';
import { ActionTypes } from '../actions/types';
import { apiQl } from 'lib/functions';

function* postGreeting(action: ActionTypes) {
  const queryQl = `mutation createGreeting(
    $name: String!
  ) {
      createGreeting(input: {
          name: $name
      }) {
          greeting {
              id
          }
      }
  }`;


  const variables = {
     name: action.payload.name,        // *** ERROR IS THROWN HERE ***
  };


  try {
    const data = yield call(apiQl, queryQl, variables);
    if (data.errors) {
        yield put(
            actionPostGreetingError({
                error: data.errors,
            }),
        );
    } else {
        yield put(
            actionPostGreetingOk({
                greeting: data.data.createGreeting,
            }),
        );
    }
} catch (error) {
    if (error.response === undefined || error.code === 'ECONNABORTED') {
        console.log('ERROR RESPONSE UNDEFINED, ABORTED');
    }
}

export default function* greeting() {
   yield all([takeLatest(Actions.POST_GREETING, postGreeting)]);
}

On the types file:

...
export interface Input {
   name: string;
}
export interface ActionPostGreeting {
   type: typeof Actions.POST_GREETING;
   payload: Input;
}
...

export type ActionTypes =
  | ActionPostGreeting
  | ActionPostGreetingInit
  | ActionPostGreetingOk
  | ActionPostGreetingError
  | ActionHydrate;

The action creator:

export const actionPostGreeting = (payload: Input): ActionPostGreeting => {
  return {
    type: Actions.POST_GREETING,
    payload,
 };
};

Upvotes: 1

Views: 980

Answers (1)

Simon Bruneaud
Simon Bruneaud

Reputation: 2567

You are passing the whole ActionTypes union to your saga, but your function should only take a specific payload type (e.g. ActionPostGreeting). Otherwise it means your saga could be called with any payload, to fix your issue you can restrict the type like this:

function* postGreeting(action: ActionPostGreeting) {
  // do stuff

  const variables = {
    name: action.payload.name, // will be good
  };
}

Upvotes: 2

Related Questions