Ye.
Ye.

Reputation: 181

Why does Typescript throw an error when typing Redux-saga call parameter - TS2339

running into a TS2339 typing error with TS, stating that I can't find the parameter being defined in the interface type. Below are the two files that I have in question:

api-service-saga.ts

import { takeEvery, call, put } from 'redux-saga/effects';
import { Action, IBookAction } from '../actions';
import { fetchAllBooks } from '../service/books-service';
import { Book } from '../classes';

export default function* watcherSaga() {
    yield takeEvery(Action.FETCH_SEARCHABLE_BOOKS, searchableBooksSaga);
}

function* searchableBooksSaga(action: IBookAction) {
    try {
        const res = yield call(fetchAllBooks, action.param);
        const searchableBooks = res.data.reduce((books: Array<Book>, b: any) => {
            books.push(new Book(b));
            return books;
        }, [])
        yield put({ type: Action.LOAD_SEARCHABLE_BOOKS, searchableBooks });
    } catch (e) {
        yield put({ type: Action.FETCH_ERROR, error: e});
    }

}

And below is the file where I had defined the interfaces:

../actions/index.ts

import { Action } from './action-constants';
import { Book } from '../classes';

export interface IAction {
    type: Action
}

// TODO: refactor into other action files if it gets too large.

// Nav Actions

export interface INavigateAction extends IAction {
    payload: string
}

export function navigateAction(newState: any): INavigateAction {
    console.log('Navigating to::', newState);
    return { type: Action.NAVIGATE_TO, payload: newState }
}

// Book Actions

export interface IFetchSearchableBookAction extends IAction {
    param: any // TODO: properly type the parameters.
}
export function fetchSerachableBooks(param: any): IBookAction {
    console.log('Fetching Searchable Books:', param);
    return { type: Action.FETCH_SEARCHABLE_BOOKS, param }
}

export interface ILoadSearchableBooks extends IAction {
    searchableBooks: Array<Book>
}

export { Action } from './action-constants';
export type IBookAction = IFetchSearchableBookAction | ILoadSearchableBooks;

As see in the last line, I am exporting IBookAction types, one of which is the IFetchSearchableBookAction which has the param property.

Full error that I am getting:

TS2339: Property 'param' does not exist on type 'IBookAction'.
Property 'param' does not exist on type 'IFetchSearchableBookAction'

Upvotes: 0

Views: 1362

Answers (1)

paibamboo
paibamboo

Reputation: 2904

Because IBookAction can be either IFetchSearchableBookAction or ILoadSearchableBooks, and ILoadSearchableBooks does not have param prop. Meaning, it can only be 50% sure that action has param prop, but not 100%.

What if action is ILoadSearchableBooks and not IFetchSearchableBookAction, then param will probably be undefined.

Workaround is to type guard to let Typescript know that this action is indeed IFetchSearchableBookAction and not ILoadSearchableBooks.

For example:

function searchableBooksSaga(action: ILoadSearchableBooks | IFetchSearchableBookAction) {
  if ('param' in action) {
    const param = action.param;
  } else {
    const books = action.searchableBooks;
  }
}

See more info about how to type guard in Typescript here

Upvotes: 1

Related Questions