You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 08:28:38