8000 Allow module augmentation of global registry? · Issue #4328 · colinhacks/zod · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Allow module augmentation of global registry? #4328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mmkal opened this issue May 6, 2025 · 2 comments
Closed

Allow module augmentation of global registry? #4328

mmkal opened this issue May 6, 2025 · 2 comments

Comments

@mmkal
Copy link
Contributor
mmkal commented May 6, 2025

Hello 👋

I'm playing around with .meta in trpc-cli, and had a thought about how it might be useful to use it when creating CLIs. title and description are already used for positional parameter names and helptext. But right now, for defining shortopts users have to rely on trpc's .meta method on the procedure, which is a little awkward. This is what I'd like to do in future:

declare module '../node_modules/.pnpm/@zod+core@0.5.0/node_modules/@zod/core/dist/commonjs/registries.d.ts' {
  interface GlobalMeta {
    alias?: string
  }
}

test('zod4 meta', async () => {
  const router = t.router({
    add: t.procedure
      .input(
        z.tuple([
          z.string().meta({
            title: 'module',
            description: 'The name of the module to addd',
          }),
          z.object({
            saveDev: z.boolean().meta({alias: 'D'}),
            saveExact: z.boolean().meta({alias: 'E'}),
          }),
        ]),
      )
      .mutation(async ({input: [name, opts]}) => {
        return JSON.stringify([`installing ${name} with opts:`, opts, '...'])
      }),
  })

  expect(await run(router, ['add', 'left-pad'])).toMatchInlineSnapshot(
    `"["installing left-pad with opts:",{"saveDev":false,"saveExact":false},"..."]"`,
  )

  expect(await run(router, ['add', 'left-pad', '--save-dev', '--save-exact'])).toMatchInlineSnapshot(
    `"["installing left-pad with opts:",{"saveDev":true,"saveExact":true},"..."]"`,
  )

  expect(await run(router, ['add', 'left-pad', '-D', '-E'])).toMatchInlineSnapshot(
    `"["installing left-pad with opts:",{"saveDev":true,"saveExact":true},"..."]"`,
  )

  expect(await run(router, ['add', 'left-pad', '-DE'])).toMatchInlineSnapshot(
    `"["installing left-pad with opts:",{"saveDev":true,"saveExact":true},"..."]"`,
  )
})

(run(...) is a helper method that roughly acts like doing node mycli.js add left-pad --save-dev --save-exact)

The zod-related part is the module augmentation that adds alias?: string to zod's GlobalMeta. I know it would be possible to use a custom registry, but it seems overkill for this - especially given this is only for type safety. Users can already do .meta({alias: 'D'}) and everything just works.

The problem with the above is the fact that GlobalMeta is part of @zod/core, not zod, so I have to use an installation-specific relative path - not very useful if I'm publishing a package. I think if GlobalMeta was part of zod I could just do:

declare module 'zod' {
  interface GlobalMeta {
    alias?: string
  }
}

I'm not sure exactly the best way to do that - is zod now just a thin wrapper for @zod/core?

@samchungy
Copy link
Contributor

You should be able to extend it via @zod/core

declare module '@zod/core' {
  interface GlobalMeta {
    foo?: string;
  }
}

@colinhacks
Copy link
Owner

Another problem solved by the new subpath approach! #4371

I've confirmed that you can extend this interface with a regular "zod/v4/core" import. Actually you can use "zod/v4" and "zod/v4-mini" as well because they re-export the same interface from core.

Notably, augmentations made to the interface propagate globally since they're all ultimately referencing the same interface.

import * as z from "zod/v4";
import * as zm from "zod/v4-mini";

declare module "zod/v4" { // or "zod/v4/core" or "zod/v4-mini"
  interface GlobalMeta {
    birthday: Date;
  }
}

z.string().meta({
  birthday: new Date(),
});

zm.string().register(zm.globalRegistry, {
  birthday: new Date(),
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
0