Angular NgRx构造函数不同时联合类型Action无法识别求助
解决NgRx Action联合类型中payload不存在的TypeScript错误(无需修改构造函数)
问题场景
现有以下NgRx Action类及联合类型定义:
export class Login implements Action { readonly type = LOGIN; constructor( public payload: { userID: string; email: string; token: string; expirationDate: Date; } ) {} } export class Logout implements Action { readonly type = LOGOUT; constructor() {} } export class LoginStart implements Action { readonly type: string = LOGIN_START; constructor(public payload: { username: string; password: string }) {} } export class LoginFail implements Action { readonly type: string = LOGIN_FAIL; constructor(public payload: string) {} } export type AuthActions = Login | Logout | LoginStart | LoginFail;
运行ng serve时出现TypeScript错误:
ERROR in src/app/auth/store/auth.reducer.ts:23:16 - error TS2339: Property 'payload' does not exist on type 'AuthActions'. Property 'payload' does not exist on type 'Logout'. 23 action.payload.userID, ~~~~~~~ src/app/auth/store/auth.reducer.ts:24:16 - error TS2339: Property 'payload' does not exist on type 'AuthActions'. Property 'payload' does not exist on type 'Logout'. 24 action.payload.email, ~~~~~~~ src/app/auth/store/auth.reducer.ts:25:16 - error TS2339: Property 'payload' does not exist on type 'AuthActions'. Property 'payload' does not exist on type 'Logout'. 25 action.payload.token,
错误原因
AuthActions是联合类型,其中Logout类没有定义payload属性。TypeScript会严格检查联合类型的属性访问,只有所有成员都具备的属性才能直接访问,因此直接读取action.payload会触发类型错误。
解决方案(无需修改Action构造函数)
在reducer中通过switch case语句基于action.type做类型窄化,让TypeScript自动推断每个分支中Action的具体类型,从而安全访问对应类型的payload属性:
import { AuthActions, LOGIN, LOGOUT, LOGIN_START, LOGIN_FAIL } from './auth.actions'; export interface AuthState { userID: string | null; email: string | null; token: string | null; loading: boolean; error: string | null; } const initialState: AuthState = { userID: null, email: null, token: null, loading: false, error: null }; export function authReducer(state = initialState, action: AuthActions): AuthState { switch (action.type) { case LOGIN: // TypeScript推断action为Login类型,可安全访问payload return { ...state, userID: action.payload.userID, email: action.payload.email, token: action.payload.token, loading: false, error: null }; case LOGOUT: // TypeScript推断action为Logout类型,无需访问payload return { ...state, userID: null, email: null, token: null, error: null }; case LOGIN_START: // TypeScript推断action为LoginStart类型,可安全访问payload return { ...state, loading: true, error: null }; case LOGIN_FAIL: // TypeScript推断action为LoginFail类型,可安全访问payload return { ...state, loading: false, error: action.payload }; default: return state; } }
原理说明
通过switch case匹配action.type,TypeScript会自动将每个分支中的action类型窄化为对应具体的Action类。此时在LOGIN分支中,TypeScript明确知道action是Login实例,因此允许访问其payload属性;而LOGOUT分支中,TypeScript知道action是Logout实例,不会允许访问不存在的payload,从根源避免了类型错误。
内容的提问来源于stack exchange,提问作者Viraj
相关产品推荐
相关产品推荐

