Created
August 12, 2026 07:46
-
-
Save cometkim/2c7bf4ac617a9a57c74d0c676ce8abec to your computer and use it in GitHub Desktop.
Enum primitive for Zod codebases
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as z from 'zod'; | |
| type ReservedFields = 'Schema' | 'KeyAsInputSchema' | 'meta' | '$Key' | '$Type'; | |
| type EnumDefinition<K extends string, V extends string | number> = { | |
| readonly [key in K]: { | |
| value: V; | |
| description?: string; | |
| }; | |
| } & { | |
| readonly [key in ReservedFields]?: never; | |
| }; | |
| type EnumMeta< | |
| Def extends EnumDefinition<string, string | number>, | |
| K extends keyof Def = keyof Def, | |
| V extends Def[K]['value'] = Def[K]['value'], | |
| > = { | |
| definition: Def; | |
| keys: readonly K[]; | |
| values: readonly V[]; | |
| }; | |
| export type Enum< | |
| Def extends EnumDefinition<string, string | number>, | |
| K extends keyof Def = keyof Def, | |
| V extends Def[K]['value'] = Def[K]['value'], | |
| > = { | |
| [Key in K]: Def[Key]['value'] & { $Description: Def[Key]['description'] }; | |
| } & { | |
| meta: EnumMeta<Def>; | |
| Schema: z.ZodEnum<{ [Key in K]: Def[Key]['value'] }>; | |
| KeyAsInputSchema: z.ZodPipe< | |
| z.ZodEnum<{ [Key in K]: Key extends string ? Key : never }>, | |
| z.ZodTransform<V, K> | |
| >; | |
| $Key: K; | |
| $Type: V; | |
| }; | |
| export type $EnumKeys<T> = T extends Enum<infer Def> ? keyof Def : never; | |
| export type $EnumValues<T> = T extends Enum<infer Def> | |
| ? Def[keyof Def]['value'] | |
| : never; | |
| /** | |
| * Define an enum | |
| * | |
| * @example | |
| * ```ts | |
| * const StatusEnum = defineEnum({ | |
| * Active: { value: 'active' }, | |
| * Inactive: { value: 'inactive' }, | |
| * }); | |
| * ``` | |
| * | |
| * @example | |
| * ```ts | |
| * StatusEnum.Active; // 'active' | |
| * StatusEnum.Schema; // z.enum(['active', 'inactive']) | |
| * | |
| * typeof StatusEnum.$Key; // 'Active' | 'Inactive' | |
| * typeof StatusEnum.$Type; // 'active' | 'inactive' | |
| * ``` | |
| * | |
| * Enum values are always normalized as $Value. | |
| * Use KeyAsInputSchema when using $Key as value. | |
| * | |
| * @example | |
| * ```ts | |
| * StatusEnum.KeyAsInputSchema.parse('Active'); // 'active' | |
| * ``` | |
| */ | |
| export function defineEnum<const Def extends EnumDefinition<string, string>>( | |
| definition: Def, | |
| ): Enum<Def> { | |
| const keys: string[] = []; | |
| const values: string[] = []; | |
| const enumShape: Record<string, string> = {}; | |
| for (const key in definition) { | |
| const { value } = definition[key]; | |
| keys.push(key); | |
| values.push(value); | |
| enumShape[key] = value; | |
| } | |
| const Schema = z.enum(enumShape) as any; | |
| const KeyAsInputSchema = z.enum(keys).transform(key => enumShape[key]) as any; | |
| const meta: EnumMeta<Def> = { | |
| definition, | |
| keys, | |
| values, | |
| }; | |
| return Object.freeze({ | |
| ...enumShape, | |
| meta, | |
| Schema, | |
| KeyAsInputSchema, | |
| get $Key(): any { | |
| throw new TypeError( | |
| "Don't use $Key directly. Use it as a type annotation only.", | |
| ); | |
| }, | |
| get $Type(): any { | |
| throw new TypeError( | |
| "Don't use $Type directly. Use it as a type annotation only.", | |
| ); | |
| }, | |
| }) as Enum<Def>; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment