javascript - Typescript reducer's switch-case typvakt fungerar inte med objektspreddning


Jag har en reducer som utför olika åtgärder beroende på action.type, åtgärdernas payload är olika för vissa åtgärder.

    export enum ActionType {
      UpdateEntireState = "UPDATE_ENTIRE_STATE",
      UpdateStateItem = "UPDATE_STATE_ITEM"
    }
    
    type TypeEditData = {
      id: string;
      name: string;
      surname: string;
      age: number;
    };
    
    export type State = TypeEditData[];
    export type Action = UpdateEntireState | UpdateStateItem;
    
    type UpdateEntireState = {
      type: ActionType.UpdateEntireState;
      payload: State;
    };
    
    type UpdateStateItem = {
      type: ActionType.UpdateStateItem;
      payload: { id: string; data: TypeEditData };
    };
    
    export function reducer(state: State, action: Action): State {
      const { type, payload } = action;
    
      switch (type) {
        case ActionType.UpdateEntireState: {
          return [...payload];
        }
        case ActionType.UpdateStateItem: {
          const person = state.filter((item) => item.id === payload.id);
          return [...state, person[0]];
        }
        default: {
          throw Error("Wrong type of action!");
        }
      }
    }

Den här koden kommer inte att fungera, felen säger att min åtgärdspayload kan vara State eller { id: string; data: TypeEditData }. Men om jag åtkomst till payload-egenskapen inuti switch-fallet med punktnotation på detta sätt

return [...action.payload];

Det kommer inte att bli några fel och typkontrollen kommer att fungera bra. Hur const { type, payload }=action; skiljer sig från action.type och action.payload när det gäller typer och varför fungerar inte typkontrollen med spridningssyntax?

TS version - 4.3.4

Svar #1

Problemet är att du har definierat payload innan det fanns typinformation tillgänglig på action, så det har unionstypen

State | {
    id: string;
    data: TypeEditData;
};

Definiera en lokal variabel eller använd helt enkelt action.payload inom varje case-sats och kompilatorn vet vilken typ den har:

export function reducer(state: State, action: Action): State {
  // const { type, payload } = action;

  switch (action.type) {
    case ActionType.UpdateEntireState: {
      return [...action.payload];
    }
    case ActionType.UpdateStateItem: {
      const person = state.filter((item) => item.id === action.payload.id);
      return [...state, person[0]];
    }
    default: {
      throw Error("Wrong type of action!");
    }
  }
}

Variabeltypen fastställs explicit vid deklaration (t.ex. const a: string) eller implicit vid initialisering (t.ex. a=4). Efterföljande typkontrollskonstruktioner används inte för att utvärdera om variabelns typ har ändrats. Tvärtom, eftersom variabelns typ redan är definierad vid den punkten används den typen för att kontrollera om det efterföljande konstruktionen är giltig för variabeln.

Svar #2

Åtgärdsgränssnittet har som standard egenskapen typ.

export interface Action {
    type: string;
}

Om du kan utöka Action-gränssnittet för att lägga till payload som en array av objekt kommer TypeScript inte att ge dig ett fel. Något i stil med detta, i din reducer-funktion kan du använda det så här

interface CustomAction extends Action{
    payload: Array<any>
}


export function reducer(state: State, action: CustomAction): State {

Relaterade artiklar