Попытка сделать обязательным наличие хотя бы одного необязательного параметра, но его наличие исключает другие

#javascript #typescript #types

#javascript #typescript #типы

Вопрос:

итак, позвольте мне углубиться в пример:

 type NodeOrMethod<T> =
    | {
          node?: Array<Filter<T>>;
          method: Condition<T>;
      }
    | {
          node: Array<Filter<T>>;
          method?: Condition<T>;
      };

interface BaseFilter {
    label: string;
    value?: string;
}

export type Filter<T> = BaseFilter amp; NodeOrMethod<T>;
  

По сути, я хочу сделать так, чтобы разработчик должен был либо включить node , либо method — что в настоящее время работает; однако я хочу сделать еще один шаг и сказать, что ЕСЛИ один из них присутствует, другого БЫТЬ НЕ МОЖЕТ. Поэтому, если они попытаются включить оба node и method в один и тот же объект, он будет жаловаться. Кто-нибудь делал что-то подобное раньше?

Комментарии:

1. Просто удалите необязательный тип?

2. @JonasWilms, похоже, это не работает. Если я добавлю как узел, так и метод, он не жалуется.

3. хм. Я всегда делаю { type: "one", stuff } | { type: "two", other } , и это работает. Кажется, что тип объединения похож на «ИЛИ» и является исключительным только в том случае, если оба типа взаимно исключают друг друга.

4. Что произойдет, если вы это сделаете method: never (к сожалению, я AFK, поэтому я не могу попробовать это сам)

5. @JonasWilms Хм, я никогда не использовал never , я просто попробовал, и это кое-что испортило, но у меня могло быть странное форматирование.

Ответ №1:

Я бы, вероятно, использовал помеченные объединения (см. Соответствующий раздел документов) для того, что вы пытаетесь сделать, что-то вроде

 type Node<T> = {
    type: 'node',
    node: Array<Filter<T>>;
}
type Method<T> = {
    type: 'method',
    method: Condition<T>;
}
type NodeOrMethod<T> = Node<T> | Method<T>;

cost a: NodeOrMethod<T> = { // valid
  type: 'node',
  node: [...]
}

cost b: NodeOrMethod<T> = { // valid
  type: 'method',
  method: ...
}

cost c: NodeOrMethod<T> = { // type error
  type: 'method',
  node: [...],
  method: ...
}
  

<a rel="noreferrer noopener nofollow" href="https://www.typescriptlang.org/play/#src=type NodeT = {
type: ‘node’,
node: T
}

type MethodT = {
type: ‘method’,
method: T
}

type NodeOrMethod = NodeT | MethodT

const a: NodeOrMethod = {
type: ‘method’,
method: 123
}

const b: NodeOrMethod = {
type: ‘node’,
node: 123
}

const c: NodeOrMethod = {
type: ‘method’,
node: 123
}» rel=»nofollow noreferrer»>Пример

Комментарии:

1. Я чувствую, что это должно сработать для меня, но по какой-то причине это не так. Он по-прежнему позволяет присутствовать обоим node и method . Он просто жалуется, когда ни того, ни другого нет. Конечно, у меня там нет type свойства.

2. Я добавил ссылку на (упрощенную) демонстрационную версию playground

3. «У меня там нет свойства type», вот что делает его взаимоисключающим.

4. @JonasWilms хорошо, итак, мне нужно иметь подобное свойство, чтобы явно указать, какой из них там есть?

5. Да вроде того. Но я не знаю фактической семантики типа объединения, чтобы дать адекватный ответ (я все еще жду прибытия одного из этих ts goldbadgers), но, например, { exists: true, stuff } | { exists: false, other } также будет работать

Ответ №2:

Философия узла / JS на самом деле так не работает. Объектам предоставляется разрешение иметь на них все, что они хотят, и потребляющий код обращает внимание только на то, что его волнует. Некоторые способы справиться с этим включают:

  • Используйте заводские методы для обеспечения правильного создания экземпляров ваших объектов
  • Имейте свойство «тип», которое указывает, смотреть ли на узел или метод

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

Комментарии:

1. Ваш первый абзац не имеет особого смысла, когда речь идет о Typescript, поскольку объекты имеют определенную форму. Фабричные методы помогли бы обеспечить определенную форму, но также и правильную типизацию. «тип подозрительно перегружен» ну что ж, в этом и заключается суть Typescript.

2. @JonasWilms Typescript не делает ничего, чтобы гарантировать, что у объекта нет свойств, которых я не ожидаю. Рассмотрите возможность расширения объекта или использования пересечения типов. Typescript стремится обеспечить доступность интерфейса, вот и все. Не то чтобы он не доступен. Он не делает этого, потому что пытаться бессмысленно и путает проблемы времени компиляции и выполнения.