Как извлечь один тип из типа союза Zod?

Я использую Zod , и у меня есть массив, содержащий различные объекты, использующие union. После разбора я хочу перебрать каждый элемент и извлечь его «настоящий» тип/отрезать другие типы.

При проверке определенных свойств объекта отлично работает следующий код:

      const objectWithNumber = zod.object({ num: zod.number() });
const objectWithBoolean = zod.object({ isTruthy: zod.boolean() });
const myArray = zod.array(zod.union([objectWithNumber, objectWithBoolean]));
const parsedArray = myArray.parse([{ isTruthy: true }, { num: 3 }]);

parsedArray.forEach((item) => {
  if ("num" in item) {
    console.info('objectWithNumber:', item);
    // TS knows about it => syntax support for objectWithNumber
  } else if ("isTruthy" in item) {
    console.info('objectWithBoolean:', item);
    // TS knows about it => syntax support for objectWithBoolean
  } else {
    console.error('unknown');
  }
});

Альтернативой может быть использование для этого дискриминационных союзов .

      const objectWithNumber = zod.object({ type: zod.literal("objectWithNumber"), num: zod.number() });
const objectWithBoolean = zod.object({ type: zod.literal("objectWithBoolean"), isTruthy: zod.boolean() });
const myArray = zod.array(zod.discriminatedUnion("type", [ objectWithNumber, objectWithBoolean ]));
const parsedArray = myArray.parse([{ type: "objectWithBoolean", isTruthy: true }, { type: "objectWithNumber", num: 3 }]);

parsedArray.forEach(item => {
  if (item.type === "objectWithNumber") {
    console.info('objectWithNumber:', item);
    // TS knows about it => syntax support for objectWithNumber
  } else if (item.type === "objectWithBoolean") {
    console.info('objectWithBoolean:', item);
    // TS knows about it => syntax support for objectWithBoolean
  } else {
    console.error('unknown');
  }
});

но я думаю, что неправильно понял эту концепцию, потому что нужно написать больше кода (я всегда могу добавить общее свойство и проверить его). Любая помощь в этом очень ценится :)

Существуют ли лучшие способы определения конкретной схемы?

1 ответ

Если я вас правильно понял, ваш вопрос сводится к тому, «Почему следует использовать размеченные союзы вместо общих полей в сочетании с необязательными полями». ( zod.jsпросто переносит эту концепцию в среду выполнения, обеспечивая функциональность проверки). В вашем примере на самом деле нет причин использовать дискриминатор ( typeсвойство), потому что каждый объект содержит только одно необязательное свойство, которое является взаимоисключающим между типами и может легко использоваться для различения типов. Однако основная проблема с этим кодом заключается в том, что имена объектов (или формы/структуры) не сообщают о каких-либо намерениях , поэтому трудно увидеть преимущества разграниченных объединений.

Вы можете думать о размеченном объединении как о типе, который описывает семейство объектов и предоставляет унифицированный механизм (в виде свойства дискриминатора) для их идентификации. Этот подход менее уязвим, чем проверка существования некоторых выбранных вручную свойств (например, свойства экземпляра). Что если numпо какой-либо причине становится необязательным? Тогда ваша проверка существования num сломается. Другим аргументом в пользу размеченного объединения является уменьшение количества необязательных свойств. Сравните два следующих примера:

      // shared and optional fields instead of discriminated union
type Vehicle = {
  name: string;
  combustionEngine?: PetrolEngine | DieselEngine;
  tankCapacity?: number;
  electricEngine?: ElectricEngine;
  batteryCapacity?: number;
}
      type Vehicle = 
  | GasolineCar
  | ElectricCar

type = GasolineCar {
  kind: "gasolineCar";
  name: string;
  engine: PetrolEngine | DieselEngine;
  tankCapacity: number;
}

type ElectricCar  = {
  kind: "electricCar"
  name: string;
  engine: ElecticEngine;
  batteryCapacity: number;
}

Пример с размеченным объединением дает гораздо более описательный код. Вам не нужно добавлять несколько проверок для необязательных полей — вместо этого просто определите (как можно раньше) тип по дискриминатору и передайте объект функции/методу, принимающему более узкий тип ( GasolineCarили же ElectricCarвместо Vehicle).

Другие вопросы по тегам