TheSmartMonkey
TheSmartMonkey

Reputation: 1052

How can I properly handle optional in TypeScript with zod infer

I'm using TypeScript with Zod to define DTO (Data Transfer Object) schemas for my API. Here's a simplified version of my code:

import { z } from 'zod';

export type DtoObject = {
  body?: z.ZodType;
  params?: z.ZodType;
  queryParams?: z.ZodType;
};

export const getTodoByIdDto: DtoObject = {
  params: z
    .object({
      todoId: z.string().openapi({ example: '1234' }),
    }),
};

export type GetTodoById = {
  params: typeof getTodoByIdDto.params extends z.ZodType ? z.infer<typeof getTodoByIdDto.params> : undefined;
};

export const sample: GetTodoById = {
  params: {
    todoId: '1234',
  },
};

However, TypeScript throws the following error:

Type '{ todoId: string; }' is not assignable to type 'undefined'.ts(2322) get-todo-by-id.dto.ts(18, 3): The expected type comes from property 'params' which is declared here on type 'GetTodoById'

It seems the type is not inferred correctly

Question

How can I properly define DtoObject to allow undefined values without causing a TypeScript error? Is there a better way to handle optional Zod schemas?

Upvotes: 0

Views: 18

Answers (1)

TheSmartMonkey
TheSmartMonkey

Reputation: 1052

Quick answer

Use satisfies instead of changing the type with DtoObject

How satisfies Works

When you use satisfies, you're telling TypeScript to check that getTodoByIdDto satisfies the shape defined by DtoObject, but without changing its type. It allows you to be more precise in ensuring that the object conforms to the expected structure without affecting its type inference

Final code

import { z } from 'zod';

export type DtoObject = {
  body?: z.ZodType;
  params?: z.ZodType;
  queryParams?: z.ZodType;
};

export const getTodoByIdDto = {
  params: z.object({
    todoId: z.string().openapi({ example: '1234' }),
  }),
} satisfies DtoObject;

export type GetTodoById = {
  params: z.infer<typeof getTodoByIdDto.params>;
};

export const sample: GetTodoById = {
  params: { // (property) params: {todoId: string;}
    todoId: '1234',
  },
};

Upvotes: 0

Related Questions