zod icon indicating copy to clipboard operation
zod copied to clipboard

v4: z.interface() not suport jsDoc

Open dbachelor110 opened this issue 8 months ago • 3 comments

I noticed an issue where JSDoc comments, such as @deprecated, are not preserved after using z.infer on z.interface(), while they are correctly retained when using z.object().

📷 Screenshot

In the screenshot below, you can see that the @deprecated annotation appears in the inferred type from z.object(), but not from z.interface():

Image

🧪 Reproduction

import * as z from 'zod';

const interfaceSchema = z.interface({
  /**
   * hellow value
   *
   * @deprecated
   */
  hellow: z.string()
});

const objectSchema = z.object({
  /**
   * hellow value
   *
   * @deprecated
   */
  hellow: z.string()
});

type ZInterface = z.infer<typeof interfaceSchema>;

const zInterface: ZInterface = { hellow: 'hellow' };

zInterface.hellow

type ZObject = z.infer<typeof objectSchema>;

const zObject: ZObject = { hellow: 'hellow' };

zObject.hellow

🧠 Expected Behavior

I expected z.infer<typeof interfaceSchema> to retain JSDoc metadata like @deprecated, just like z.object() does.

dbachelor110 avatar Apr 11 '25 17:04 dbachelor110

How about adding a method .deprecated() and adding the schema to the metadata?

https://json-schema.org/draft/2020-12/json-schema-validation#section-9.3

PandaWorker avatar Apr 11 '25 17:04 PandaWorker

@PandaWorker

That might be good in some cases, but not for mine — I should've included my use case in the first comment.

In my use case, I rely on z.infer to define my DTOs, so I can avoid duplicating type definitions and keep validation logic decoupled from TypeScript interfaces.

However, when JSDoc annotations (not just @deprecated, but any comments) are lost in the inferred type, I’m forced to manually define a separate TypeScript interface just to preserve documentation metadata — which defeats part of the purpose of using z.infer.

Additionally, if z.object could support getters for defining cyclical properties, that would be great. But I’m not sure whether using getters is what causes the JSDoc loss or not.

lawsnote-hydra avatar Apr 12 '25 09:04 lawsnote-hydra

I would wager it's the optional key behaviour that strips the JSDoc

samchungy avatar Apr 13 '25 05:04 samchungy

@samchungy

Yes, you're right. But I've found that any type inference or transformation causes JSDoc to be lost, including:

  • z.interface()
  • z.any().optional()

At this point, I believe this isn't a bug in Zod per se — it's a TypeScript limitation.

Here’s a minimal example:

const original = {
  /** user id */
  id: true,
} as const;

type Original = typeof original;

const transformed = {
  get id() {
    return original.id;
  },
};

transformed.id; // JSDoc lost

const typeInfer = () => original;

typeInfer().id; // JSDoc lost

It might be helpful if Zod exposes something like an originalShape property — so that others who want to build a CLI tool or a TypeDoc plugin to work around this limitation can still access the original input object, where the JSDoc annotations are preserved.

dbachelor110 avatar Apr 14 '25 02:04 dbachelor110

z.interface() has been removed since this issue was open. z.object() continues to support JSDoc.

colinhacks avatar May 15 '25 05:05 colinhacks