Как получить доступ к свойству конкретной ветви Zod DiscriminatedUnion из функции валидации “refine”?

Вопрос или проблема

Вот код, отвечающий за валидацию схемы формы в нашем фронтенд-приложении.

У нас возникают проблемы с библиотекой Zod при доступе к свойству конкретной ветви discriminatedUnion из колбека валидации refine(). Свойство endDate отсутствует в автозаполнении, свойство подчеркивается красным языковым сервером Typescript с сообщением “Свойство ‘endDate’ не существует для типа …”.

Как должны быть доступны свойства конкретных ветвей в этом контексте?
export const formSchema = z.object({
  // Несколько полей здесь ...
  contract: z.object({
    startDate: z.string().describe('текст'),
    contractType: z.enum(['III', 'DDD']).describe('выбор'),
    // Несколько других полей здесь ...
    specificationContrat: z.discriminatedUnion('contractType', [
      z.object({
        contractType: z.literal('III'),
        ...someSchema.shape,
      }),
      z.object({
        contractType: z.literal('DDD'),
        endDate: z.string().describe('текст'),
        ...someSchema.shape,
      })
    ]),
  })
  .refine(
    (contract) => {
      return contract.contractType === 'III' ||
        (contract.contractType === 'DDD' && new Date(contract.specificationContrat.endDate) > new Date(contract.startDate));
        // "Свойство 'endDate' не существует для типа …"______________________________^
    },
    '"endDate" должно быть ранее "startDate"'
  )
});

Ответ или решение

Для решения проблемы с валидацией с использованием библиотеки Zod и доступом к специфическим свойствам ветвей типа discriminatedUnion, необходимо учитывать, что TypeScript не может гарантировать, что определенное свойство доступно для всех ветвей объединения. В вашем случае, свойство endDate отсутствует в ветви, соответствующей типу III, что и приводит к ошибке компиляции.

Чтобы получить доступ к свойству endDate в функции валидации refine, следует использовать метод приведения типов. Это можно сделать с помощью конструкции if для проверки типа контракта перед обращением к свойству. Вот как можно переписать вашу функцию валидации для обеспечения правильного доступа к endDate:

import { z } from 'zod';

const formSchema = z.object({
  // Другие поля ...
  contract: z.object({
    startDate: z.string().describe('text'),
    contractType: z.enum(['III', 'DDD']).describe('select'),
    // Другие поля ...
    specificationContrat: z.discriminatedUnion('contractType', [
      z.object({
        contractType: z.literal('III'),
        // Поля для контракта типа III...
      }),
      z.object({
        contractType: z.literal('DDD'),
        endDate: z.string().describe('text'),
        // Поля для контракта типа DDD...
      })
    ]),
  })
  .refine((contract) => {
    if (contract.contractType === 'III') {
      return true; // Валидация для III типа
    } else if (contract.contractType === 'DDD') {
      const endDate = contract.specificationContrat.endDate;
      return new Date(endDate) > new Date(contract.startDate);
    }
    return false; // На всякий случай, если контрактType не соответствует ни одному из вариантов
  }, '"endDate" должен быть позднее "startDate"')
});

Разбор решения:

  1. Проверка типа: Используя конструкцию if, мы проверяем тип контракта до того, как мы обращаемся к endDate. Это позволяет TypeScript гарантировать, что мы работаем с ветвью, которая действительно содержит это свойство.

  2. Чистота кода: Функция валидации остаётся понятной и модульной, так как мы явно обрабатываем оба случая. Если тип контракта не соответствует ни одному из ожидаемых значений, возвращаем false для предотвращения неявного поведения.

  3. Поддержка TypeScript: Данное решение устраняет проблемы с подчеркиванием в IDE, так как TypeScript теперь может извлечь информацию о типах на основе проверок, во избежание доступа к несуществующим свойствам.

Понимание работы discriminatedUnion и правильная работа с конкретными ветвями являются ключевыми факторами при использовании библиотеки Zod для валидации данных. Следуя предложенному подходу, вы сможете избежать типовых ошибок и обеспечить корректную валидацию ваших данных.

Оцените материал
Добавить комментарий

Капча загружается...