zod icon indicating copy to clipboard operation
zod copied to clipboard

Idea: `.meta(...)` method on ZodType

Open mmkal opened this issue 1 year ago • 7 comments

I've considered asking this for a bit, for the sake of trpc-cli. So far, it takes advantage of .describe('...') to allow adding CLI docs for zod input parameters, but it would be nice to add richer metadata.

How I think it could work:

  1. Define a new ZodMeta interface (maybe make it generic with the same typeargs as ZodType)
  2. Only add a description property to it
  3. Add a metadata: ZodMeta prop to ZodType
  4. Have .describe('foo') update the metadata to { description: 'foo' } (maybe shallow-merging the old value?)
  5. Update the description property of ZodType to get description() { return this.metadata.description } for backwards-compatibility

The fun part would be: end users could then use module augmentation to add to ZodMeta however they like, e.g. I in trpc-cli could do:

import {z} from 'zod'

declare module 'zod' {
  export interface ZodMeta {
    alias: string
  }
}

const router = t.router({
  test: t.input(
    z.object({
      glob: z.string().meta({
        description: 'Glob pattern for test files',
        alias: 'g',
      })
    })
  ).mutation(...)
)

Looking at the issues, I think this would cover a few requests zod has got:

https://github.com/colinhacks/zod/issues/3734 https://github.com/colinhacks/zod/issues/3732 https://github.com/colinhacks/zod/issues/3736 (maybe) https://github.com/colinhacks/zod/issues/3673 https://github.com/colinhacks/zod/issues/1439

Possible more but I didn't go that far back in the issues. In general, with trpc and OpenAI adopting zod, there will be more cases where people use zod as a user-facing part of their libraries and applications, so there might be more and more requests for the ability to attach metadat to types

Alternatively, these fields could somehow be dumped onto ZodDef, which is where description lives now but that doesn't feel quite right to me.

mmkal avatar Sep 06 '24 17:09 mmkal

i like the idea of removing all the non entity related stuff to a meta field, it would make it more consistent across types

klh avatar Sep 13 '24 07:09 klh

I am a long time zod user and had wanted something similar. There is this library zod-to-openapi that introspects metadata, but that has intimate knowledge of zod internals.

I'd like to share a zod compatible library I recently published. It is built with extensibility (like in this issue) in mind. I started it initially wanting to customize some zod error messages, and it kind of grew into something bigger.

https://github.com/ajaishankar/pukka?tab=readme-ov-file#extension-introspection

I apologize for the plug, but thought you'd find it interesting.

ajaishankar avatar Oct 10 '24 02:10 ajaishankar

I like it

klh avatar Oct 10 '24 18:10 klh

that'd be really awesome, I use zod schemas to generate forms in react and need to attach a lot of extra data to a ZodType. Currently I have to hack it a bit like so:

type ZodTypeEnhanced<
	O = any,
	D extends ZodTypeDef = ZodTypeDef,
	I = O
> = ZodType<O, D, I> & {
	meta: FieldMeta
	original: ZodType<O, D, I>
}

export const isEnhanced = <O, D extends ZodTypeDef, I>(
	type: any
): type is ZodTypeEnhanced<O, D, I> =>
	type._def !== undefined && type.meta !== undefined

// Create a proxy to delegate method calls
export const asField = <
	M extends FieldMeta,
	O = any,
	D extends ZodTypeDef = ZodTypeDef,
	I = O
>(
	schema: ZodType<O, D, I>,
	meta: M
): ZodTypeEnhanced<O, D, I> => {
	return new Proxy(schema, {
		get(target: any, prop: string) {
			if (prop === 'meta') {
				return meta
			}
			if (prop === 'original') {
				return schema
			}
			if (typeof target[prop] === 'function') {
				return (...args: any[]) => target[prop](...args)
			}
			return target[prop]
		}
	})
}

then all I need to do is wrap the codecs with asField like so:

object({
	...
	parent_id: asField(number().optional(), {
		label: 'Parent',
		placeholder: 'No parent',
		description: 'From which tag should values be inherited from?',
		editor: EditorType.Select,
		options: ....
	})
})

I'm pretty sure this would spark a ton of plugins for zod if it were natively supported.

mendrik avatar Nov 05 '24 19:11 mendrik

image I think we can use yup instead

wszgrcy avatar Dec 20 '24 06:12 wszgrcy

Looks like arktype has this feature: https://arktype.io/docs/expressions#meta

Also apparently this library is providing this functionality to zod: https://github.com/IvanovES/zod-metadata

smashah avatar Mar 15 '25 10:03 smashah

Hi mate, this is now available in the new zod v4. Have a look here: https://v4.zod.dev/metadata

If this answers your question, please could you close the issue? Cheers.

Christopher96u avatar Apr 11 '25 07:04 Christopher96u

It does!

mmkal avatar Jun 11 '25 20:06 mmkal