Универсальный с объединяющим типом, не назначаемая ошибка - PullRequest
0 голосов
/ 10 июня 2019

У меня есть редуктор, у которого есть создатель действий, который может быть массивом двух разных типов объектов, каждый из которых имеет свой собственный интерфейс.Тем не менее, я получаю эту ошибку

Type '(A | B)[]' is not assignable to type 'B[]'.
  Type 'A | B' is not assignable to type 'B'.
    Property 'productionId' is missing in type 'A' but required in type 'B'

Я подозреваю, что эта ошибка происходит из-за того, что оба интерфейса имеют одинаковые значения, за исключением того, что B имеет одно дополнительное значение, чем A?

Вот машинопись игровая площадка

Вот полный воспроизводимый код

interface A {
  id: number;
  name: string;
}

interface B {
  id: number;
  productionId: number;
  name: string;
}

interface IAction<Data> {
  type: string;
  data: Data;
}

interface ISelectionOptionsReducerState {
  a: A[];
  b: B[];
}

let initialState: ISelectionOptionsReducerState = {
  a: [],
  b: []
};

type TAction = IAction<Array<A | B>>;
type TAction = IAction<A[] | B[]>; <= this didn't work either

type TReducer = (
  state: ISelectionOptionsReducerState,
  action: TAction
) => ISelectionOptionsReducerState;

const selectionOptionsReducer: TReducer = (
  state: ISelectionOptionsReducerState = initialState,
  action: TAction
): ISelectionOptionsReducerState => {
  Object.freeze(state);

  let newState: ISelectionOptionsReducerState = state;

  switch (action.type) {
    case '1':
      newState.a = action.data;
      break;
    case '2':
      newState.b = action.data; <= error happen here
      break;
    default:
      return state;
  }

  return newState;
};

Ответы [ 2 ]

2 голосов
/ 10 июня 2019

Я подозреваю, что эта ошибка вызвана тем, что оба интерфейса имеют одинаковые значения, за исключением того, что B имеет одно дополнительное значение, чем A?

Да, вы можете назначить B для A, но не A дляB.

Вам нужен тип охранника:

function isA(data: A | B): data is A {
  return typeof (data as B).productionId === 'undefined'
}

function isB(data: A | B): data is B {
  return typeof (data as B).productionId === 'string'
}

... 

case '1':
  newState.a = action.data.filter(isA);
  break;
case '2':
  newState.b = action.data.filter(isB);
  break;

Редактировать: (я не могу писать комментарии)

@ Тайлер Себастьян

Array<A | B>
(A | B)[]

идентичны, но A[] | B[] отличается

1 голос
/ 10 июня 2019

Пара вещей:

Во-первых,

Array<A | B>
(A | B)[]

все идентичны.

Во-вторых, причина, по которой A назначается обоим, заключается в том, что все свойстваA также находятся в B.

В-третьих, не изменяйте состояние.Переназначение этого недостаточно.

> const x = {}
undefined
> const y = x
undefined
> y.a = 1
1
> x
{ a: 1 }

Вы можете разделиться на новый объект: let newState = { ...state } - этого вполне достаточно.

Хорошо.Вы не можете присвоить значение типа A | B чему-то, что имеет тип B.Вы использовали что-то другое (type), чтобы сигнализировать о другом значении, но TS не может знать об этом, пока вы не скажете это.Есть несколько разных способов сделать это.

Во-первых, утверждать:

newState.b = action.data as B[];

Это фактически говорит TS о том, что он должен выключиться.Чаще всего это нормально ... Если вы делаете что-то действительно сомнительное, TS заставит вас сначала заявить unknown.Это не тот случай, здесь.

, но есть и лучшие способы сделать это.

Немного лучше: наберите охрану

Это требует рефакторинга коммутатора:

function isA(x: any): x is IAction<Array<A>> {
  return x.type === '1'
}

function isB(x: any): x is IAction<Array<B>> {
  return x.type === '2'
}

...

if (isA(action)) {
  newState.a = action.data;
} else if (isB(action)) {
  newState.b = action.data;
}

( note : я не могу заставить это работать ... код правильный, я просто набираю never для действия после первой проверки - не уверен, что здесь происходит)

Наконец, пусть TypeScript выполнит для вас разрешение с помощью duck-typing .

Это означает, что если у вас есть свойство в объекте, которое коррелирует с типомTS может автоматически подбирать типы, если это свойство достаточно уникально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...