Можно ли создать интерфейс с типом свойства, который изменяется в зависимости от другого свойства, не зная об этом подробно при компиляции? - PullRequest
0 голосов
/ 08 апреля 2020

Я хочу иметь возможность выводить тип свойства объекта на основе другого его свойства без необходимости объявления T, например, newType. Вместо этого объявляя newType и выводя T из свойства. См. Ниже:

export interface Action {
  type: K;
  payload: K extends "UPDATE_FIVE_DAY"
    ? {
        fiveDayForecast?: FiveDayForecast;
        fiveDayExpiresAt?: Moment;
        fiveDayLocationFor?: Location;
      }
    : K extends "UPDATE_LOADING"
    ? { loading: boolean }
    : K extends "UPDATE_LOCATION"
    ? { location: Location }
    : K extends "UPDATE_SETTINGS"
    ? { settings: Settings }
    : undefined;
}

У меня проблема в том, что переменная K не существует, я могу настроить ее следующим образом:

export interface Action<K extends 'UPDATE_FIVE_DAY' | 'UPDATE_LOADING' | ...etc

Однако тогда мне нужно объявить тип action.type, который я не обязательно знаю. Я хочу использовать тип, как показано ниже:

Я хочу использовать это действие в обобщенном c редукторе как таковом:

export default (state: State = initialState, action: Action): State => {
  switch (action.type) {
    case "UPDATE_LOADING":
      return {
        ...state,
        loading: action.payload,
      };
  }
});

Однако оператор return выдает ошибку типа, так как считает, что action.payload может быть любым из возможных типов возврата. Что не соответствует действительности.

1 Ответ

0 голосов
/ 08 апреля 2020

Мне удалось заставить его работать с помощью Дискриминационных Союзов, как предложено @ shlang.

Редуктор:

export default (state: State = initialState, action: Actions): State => {
  switch (action.type) {
    case "UPDATE_LOADING":
      return {
        ...state,
        loading: action.payload.loading,
      };
   }
});

и типы действий:

export type Action = {
  type: AllActionTypes;
  payload: Partial<State> | undefined;
};

export type Actions =
  | UpdateFiveDayAction
  | UpdateLoadingAction;


export interface UpdateLoadingAction extends Action {
  type: "UPDATE_LOADING";
  payload: { loading: State["loading"] };
}


export interface UpdateFiveDayAction extends Action {
  type: "UPDATE_FIVE_DAY";
  payload: {
    fiveDayForecast: State["fiveDayForecast"];
    fiveDayExpiresAt: State["fiveDayExpiresAt"];
    fiveDayLocationFor: State["fiveDayLocationFor"];
  };
}
...