Reputation: 2445
I'm using TypeScript 2.7.1 and ngrx. My Actions look like this:
import { Action } from '@ngrx/store';
export const HEALTH_FORM_CONTENT_CHANGED = '[setupHealthForm] Content Changed';
export const HEALTH_FORM_TITLE_CHANGED = '[setupHealthForm] Title Changed';
export class SetupHealthFormContentChangedAction implements Action {
public type: string = HEALTH_FORM_CONTENT_CHANGED;
constructor(public payload: { content: string }) { }
}
export class SetupHealthFormTitleChangedAction implements Action {
public type: string = HEALTH_FORM_TITLE_CHANGED;
constructor(public payload: { title: string }) { }
}
export type Actions
=
SetupHealthFormContentChangedAction
| SetupHealthFormTitleChangedAction;
my Reducer looks like this:
import { Actions, HEALTH_FORM_TITLE_CHANGED, HEALTH_FORM_CONTENT_CHANGED } from './setup-health-form.actions';
export interface State {
title: string;
body: string;
}
const initialState: State = {
title: '',
body: ''
}
export function reducer(state: State = initialState, action: Actions): State {
switch (action.type) {
case HEALTH_FORM_TITLE_CHANGED: {
return {
...state,
...{ title: action.payload.title }
}
}
case HEALTH_FORM_CONTENT_CHANGED: {
return {
...state,
...{ body: action.payload.content }
}
}
default: {
return state;
}
}
}
export const body = (state: State) => state.body;
export const title = (state: State) => state.title;
However I get the following typescript errors:
error TS2339: Property 'title' does not exist on type '{ content: string; } | { title: string; }'.
error TS2339: Property 'content' does not exist on type '{ content: string; } | { title: string; }'.
The only way i've found to fix this is exporting an action with a payload of type any. How do I correctly fix this issue to retain my typings?
Upvotes: 3
Views: 2352
Reputation: 3073
Added to the existing answer of Titian, for my case, the error occurs if the type
definition is a string is concatenated (this is the behavior of my IDE setting called hard wrap to auto break if the line is too long).
You will need to make sure the type
is a concrete one single const string.
Not working
export const GET_SYSTEM_CLOSING_REPORT_OF_PROJECT_MANAGER = '[MonthClosingReportActions] Get System Closing Reports of' +
' project manager';
export class GetSystemClosingReportOfProjectManagerAction implements Action {
readonly type = GET_SYSTEM_CLOSING_REPORT_OF_PROJECT_MANAGER;
constructor(public payload: string) {}
}
Working
export const GET_SYSTEM_CLOSING_REPORT_OF_PROJECT_MANAGER = '[MonthClosingReportActions] Get System Closing Reports of project manager';
export class GetSystemClosingReportOfProjectManagerAction implements Action {
readonly type = GET_SYSTEM_CLOSING_REPORT_OF_PROJECT_MANAGER;
constructor(public payload: string) {}
}
Upvotes: 0
Reputation: 249466
To use discriminated union types and their switch
type guard behavior, the type of type
has to be a string literal type (basically a string type that can only be a single value). Your type
field is string
even through you assign a constant to it. This happens because typescript assumes that you will want ot mutate the field so it types it as string
. If you mark it as readonly
and remove the explicit string
type the field will be typed with the type of the constant (which is a string literal type) and your switch
will correctly type check:
export class SetupHealthFormContentChangedAction {
public readonly type = HEALTH_FORM_CONTENT_CHANGED;
constructor(public payload: { content: string }) { }
}
export class SetupHealthFormTitleChangedAction implements Action {
public readonly type = HEALTH_FORM_TITLE_CHANGED
constructor(public payload: { title: string }) { }
Upvotes: 5