Standalone reproduction for hey-api/openapi-ts #3941.
npm install
npm run generate
npm run typecheck # demonstrates the TS error
npx tsx src/demonstrate-data-loss.ts # demonstrates the runtime data losstsc --noEmit should pass; generated types.gen.ts should have string-quoted keys for decimal-looking x-enum-varnames; Object.keys(...) at runtime should return the original varnames byte-for-byte.
src/generated/zod.gen.ts(7,57): error TS2345: Argument of type '{ readonly 22.537: 1; readonly 22.567: 2; }' is not assignable to parameter of type 'EnumLike'.
Property '22.537' is incompatible with index signature.
Type 'number' is not assignable to type 'string'.
Schema x-enum-varnames: ["101.147", "90.20", "90.35"]
Object.keys(...): ["101.147","90.2","90.35"]
"90.20" preserved? false
"90.2" present? true
src/generated/types.gen.ts emits unquoted decimal keys:
export const IntEnumWithDecimalVarnames = {
/** 22.537 */
22.537: 1,
/** 22.567 */
22.567: 2,
} as const;
export const StringEnumWithDecimalVarnames = {
/** 101.147 */
101.147: 'FM',
/** 90.20 */
90.2: 'PS', // <-- DATA LOSS: trailing zero dropped
/** 90.35 */
90.35: 'B/ILT',
} as const;Two issues:
z.nativeEnumrejects numeric-keyed objects —src/generated/zod.gen.tsfailstsc --noEmitwith TS2345.- Data loss for string-valued enums — the OpenAPI varname
"90.20"is preserved correctly in the JSDoc, then the key beneath it is emitted unquoted, and JS coerces90.20→90.2. If two varnames were"90.20"and"90.2", they would collide silently into one key.
The input openapi.json has quoted-string varnames ("22.537", "90.20", "90.35"). The defect is in openapi-ts's TS emitter — it knows the original (preserves it in the JSDoc above) but emits the key unquoted.
Quote the emitted key when the varname isn't a valid JS identifier (or unconditionally — quoted identifier-shaped keys are still legal).