fork of hey-api/openapi-ts because I need some additional things

Merge pull request #2704 from hey-api/copilot/fix-f5d17ffa-a1b7-42cb-856a-d2277e88e4fe

authored by

Lubos and committed by
GitHub
44ccd36d 612d9c15

+901 -197
+5
.changeset/small-numbers-pull.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + fix(zod): correct schemas for numeric and boolean enums
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/webhooks/zod.gen.ts
··· 630 630 host_id: z.string(), 631 631 message_id: z.string(), 632 632 inviter_name: z.string(), 633 - reason_type: z.unknown(), 633 + reason_type: z.union([ 634 + z.literal(0), 635 + z.literal(1), 636 + z.literal(2), 637 + z.literal(3), 638 + z.literal(4), 639 + z.literal(7), 640 + z.literal(8), 641 + z.literal(9), 642 + z.literal(10), 643 + z.literal(11), 644 + z.literal(12), 645 + z.literal(13), 646 + z.literal(14) 647 + ]), 634 648 participant: z.object({ 635 649 call_type: z.string(), 636 650 device_ip: z.string()
+23 -3
packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts
··· 103 103 /** 104 104 * This is a simple enum with numbers 105 105 */ 106 - export const zEnumWithNumbers = z.unknown(); 106 + export const zEnumWithNumbers = z.union([ 107 + z.literal(1), 108 + z.literal(2), 109 + z.literal(3), 110 + z.literal(1.1), 111 + z.literal(1.2), 112 + z.literal(1.3), 113 + z.literal(100), 114 + z.literal(200), 115 + z.literal(300), 116 + z.literal(-100), 117 + z.literal(-200), 118 + z.literal(-300), 119 + z.literal(-1.1), 120 + z.literal(-1.2), 121 + z.literal(-1.3) 122 + ]); 107 123 108 124 /** 109 125 * Success=1,Warning=2,Error=3 ··· 113 129 /** 114 130 * This is a simple enum with numbers 115 131 */ 116 - export const zEnumWithExtensions = z.unknown(); 132 + export const zEnumWithExtensions = z.union([ 133 + z.literal(200), 134 + z.literal(400), 135 + z.literal(500) 136 + ]); 117 137 118 138 /** 119 139 * This is a simple array with numbers ··· 234 254 '500 foo.bar', 235 255 '600 foo&bar' 236 256 ])), 237 - bool: z.optional(z.unknown()) 257 + bool: z.optional(z.literal(true)) 238 258 }); 239 259 240 260 /**
+23 -3
packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts
··· 103 103 /** 104 104 * This is a simple enum with numbers 105 105 */ 106 - export const zEnumWithNumbers = z.unknown(); 106 + export const zEnumWithNumbers = z.union([ 107 + z.literal(1), 108 + z.literal(2), 109 + z.literal(3), 110 + z.literal(1.1), 111 + z.literal(1.2), 112 + z.literal(1.3), 113 + z.literal(100), 114 + z.literal(200), 115 + z.literal(300), 116 + z.literal(-100), 117 + z.literal(-200), 118 + z.literal(-300), 119 + z.literal(-1.1), 120 + z.literal(-1.2), 121 + z.literal(-1.3) 122 + ]); 107 123 108 124 /** 109 125 * Success=1,Warning=2,Error=3 ··· 113 129 /** 114 130 * This is a simple enum with numbers 115 131 */ 116 - export const zEnumWithExtensions = z.unknown(); 132 + export const zEnumWithExtensions = z.union([ 133 + z.literal(200), 134 + z.literal(400), 135 + z.literal(500) 136 + ]); 117 137 118 138 /** 119 139 * This is a simple array with numbers ··· 234 254 '500 foo.bar', 235 255 '600 foo&bar' 236 256 ]).optional(), 237 - bool: z.unknown().optional() 257 + bool: z.literal(true).optional() 238 258 }); 239 259 240 260 /**
+23 -3
packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts
··· 103 103 /** 104 104 * This is a simple enum with numbers 105 105 */ 106 - export const zEnumWithNumbers = z.unknown(); 106 + export const zEnumWithNumbers = z.union([ 107 + z.literal(1), 108 + z.literal(2), 109 + z.literal(3), 110 + z.literal(1.1), 111 + z.literal(1.2), 112 + z.literal(1.3), 113 + z.literal(100), 114 + z.literal(200), 115 + z.literal(300), 116 + z.literal(-100), 117 + z.literal(-200), 118 + z.literal(-300), 119 + z.literal(-1.1), 120 + z.literal(-1.2), 121 + z.literal(-1.3) 122 + ]); 107 123 108 124 /** 109 125 * Success=1,Warning=2,Error=3 ··· 113 129 /** 114 130 * This is a simple enum with numbers 115 131 */ 116 - export const zEnumWithExtensions = z.unknown(); 132 + export const zEnumWithExtensions = z.union([ 133 + z.literal(200), 134 + z.literal(400), 135 + z.literal(500) 136 + ]); 117 137 118 138 /** 119 139 * This is a simple array with numbers ··· 234 254 '500 foo.bar', 235 255 '600 foo&bar' 236 256 ])), 237 - bool: z.optional(z.unknown()) 257 + bool: z.optional(z.literal(true)) 238 258 }); 239 259 240 260 /**
+44 -11
packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts
··· 119 119 'Non-ascii: øæåôöØÆÅÔÖ字符串' 120 120 ]); 121 121 122 - export const zEnumWithReplacedCharacters = z.enum([ 123 - "'Single Quote'", 124 - '"Double Quotes"', 125 - 'øæåôöØÆÅÔÖ字符串', 126 - '' 122 + export const zEnumWithReplacedCharacters = z.union([ 123 + z.literal("'Single Quote'"), 124 + z.literal('"Double Quotes"'), 125 + z.literal('øæåôöØÆÅÔÖ字符串'), 126 + z.literal(3.1), 127 + z.literal('') 127 128 ]); 128 129 129 130 /** 130 131 * This is a simple enum with numbers 131 132 */ 132 - export const zEnumWithNumbers = z.unknown(); 133 + export const zEnumWithNumbers = z.union([ 134 + z.literal(1), 135 + z.literal(2), 136 + z.literal(3), 137 + z.literal(1.1), 138 + z.literal(1.2), 139 + z.literal(1.3), 140 + z.literal(100), 141 + z.literal(200), 142 + z.literal(300), 143 + z.literal(-100), 144 + z.literal(-200), 145 + z.literal(-300), 146 + z.literal(-1.1), 147 + z.literal(-1.2), 148 + z.literal(-1.3) 149 + ]); 133 150 134 151 /** 135 152 * Success=1,Warning=2,Error=3 ··· 139 156 /** 140 157 * This is a simple enum with numbers 141 158 */ 142 - export const zEnumWithExtensions = z.unknown(); 159 + export const zEnumWithExtensions = z.union([ 160 + z.literal(200), 161 + z.literal(400), 162 + z.literal(500) 163 + ]); 143 164 144 - export const zEnumWithXEnumNames = z.unknown(); 165 + export const zEnumWithXEnumNames = z.union([ 166 + z.literal(0), 167 + z.literal(1), 168 + z.literal(2) 169 + ]); 145 170 146 171 /** 147 172 * This is a simple array with numbers ··· 311 336 '500 foo.bar', 312 337 '600 foo&bar' 313 338 ])), 314 - bool: z.optional(z.unknown()) 339 + bool: z.optional(z.literal(true)) 315 340 }); 316 341 317 342 /** ··· 752 777 String: z.optional(z.enum([ 753 778 'String' 754 779 ])), 755 - number: z.optional(z.unknown()), 780 + number: z.optional(z.literal(0)), 756 781 null: z.optional(z.unknown()), 757 782 withType: z.optional(z.enum([ 758 783 'Some string' ··· 938 963 ]); 939 964 940 965 export const zModelWithNumericEnumUnion = z.object({ 941 - value: z.optional(z.unknown()) 966 + value: z.optional(z.union([ 967 + z.literal(-10), 968 + z.literal(-1), 969 + z.literal(0), 970 + z.literal(1), 971 + z.literal(3), 972 + z.literal(6), 973 + z.literal(12) 974 + ])) 942 975 }); 943 976 944 977 /**
+44 -11
packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts
··· 119 119 'Non-ascii: øæåôöØÆÅÔÖ字符串' 120 120 ]); 121 121 122 - export const zEnumWithReplacedCharacters = z.enum([ 123 - "'Single Quote'", 124 - '"Double Quotes"', 125 - 'øæåôöØÆÅÔÖ字符串', 126 - '' 122 + export const zEnumWithReplacedCharacters = z.union([ 123 + z.literal("'Single Quote'"), 124 + z.literal('"Double Quotes"'), 125 + z.literal('øæåôöØÆÅÔÖ字符串'), 126 + z.literal(3.1), 127 + z.literal('') 127 128 ]); 128 129 129 130 /** 130 131 * This is a simple enum with numbers 131 132 */ 132 - export const zEnumWithNumbers = z.unknown(); 133 + export const zEnumWithNumbers = z.union([ 134 + z.literal(1), 135 + z.literal(2), 136 + z.literal(3), 137 + z.literal(1.1), 138 + z.literal(1.2), 139 + z.literal(1.3), 140 + z.literal(100), 141 + z.literal(200), 142 + z.literal(300), 143 + z.literal(-100), 144 + z.literal(-200), 145 + z.literal(-300), 146 + z.literal(-1.1), 147 + z.literal(-1.2), 148 + z.literal(-1.3) 149 + ]); 133 150 134 151 /** 135 152 * Success=1,Warning=2,Error=3 ··· 139 156 /** 140 157 * This is a simple enum with numbers 141 158 */ 142 - export const zEnumWithExtensions = z.unknown(); 159 + export const zEnumWithExtensions = z.union([ 160 + z.literal(200), 161 + z.literal(400), 162 + z.literal(500) 163 + ]); 143 164 144 - export const zEnumWithXEnumNames = z.unknown(); 165 + export const zEnumWithXEnumNames = z.union([ 166 + z.literal(0), 167 + z.literal(1), 168 + z.literal(2) 169 + ]); 145 170 146 171 /** 147 172 * This is a simple array with numbers ··· 311 336 '500 foo.bar', 312 337 '600 foo&bar' 313 338 ]).optional(), 314 - bool: z.unknown().optional() 339 + bool: z.literal(true).optional() 315 340 }); 316 341 317 342 /** ··· 750 775 String: z.enum([ 751 776 'String' 752 777 ]).optional(), 753 - number: z.unknown().optional(), 778 + number: z.literal(0).optional(), 754 779 null: z.unknown().optional(), 755 780 withType: z.enum([ 756 781 'Some string' ··· 936 961 ]); 937 962 938 963 export const zModelWithNumericEnumUnion = z.object({ 939 - value: z.unknown().optional() 964 + value: z.union([ 965 + z.literal(-10), 966 + z.literal(-1), 967 + z.literal(0), 968 + z.literal(1), 969 + z.literal(3), 970 + z.literal(6), 971 + z.literal(12) 972 + ]).optional() 940 973 }); 941 974 942 975 /**
+44 -11
packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts
··· 119 119 'Non-ascii: øæåôöØÆÅÔÖ字符串' 120 120 ]); 121 121 122 - export const zEnumWithReplacedCharacters = z.enum([ 123 - "'Single Quote'", 124 - '"Double Quotes"', 125 - 'øæåôöØÆÅÔÖ字符串', 126 - '' 122 + export const zEnumWithReplacedCharacters = z.union([ 123 + z.literal("'Single Quote'"), 124 + z.literal('"Double Quotes"'), 125 + z.literal('øæåôöØÆÅÔÖ字符串'), 126 + z.literal(3.1), 127 + z.literal('') 127 128 ]); 128 129 129 130 /** 130 131 * This is a simple enum with numbers 131 132 */ 132 - export const zEnumWithNumbers = z.unknown(); 133 + export const zEnumWithNumbers = z.union([ 134 + z.literal(1), 135 + z.literal(2), 136 + z.literal(3), 137 + z.literal(1.1), 138 + z.literal(1.2), 139 + z.literal(1.3), 140 + z.literal(100), 141 + z.literal(200), 142 + z.literal(300), 143 + z.literal(-100), 144 + z.literal(-200), 145 + z.literal(-300), 146 + z.literal(-1.1), 147 + z.literal(-1.2), 148 + z.literal(-1.3) 149 + ]); 133 150 134 151 /** 135 152 * Success=1,Warning=2,Error=3 ··· 139 156 /** 140 157 * This is a simple enum with numbers 141 158 */ 142 - export const zEnumWithExtensions = z.unknown(); 159 + export const zEnumWithExtensions = z.union([ 160 + z.literal(200), 161 + z.literal(400), 162 + z.literal(500) 163 + ]); 143 164 144 - export const zEnumWithXEnumNames = z.unknown(); 165 + export const zEnumWithXEnumNames = z.union([ 166 + z.literal(0), 167 + z.literal(1), 168 + z.literal(2) 169 + ]); 145 170 146 171 /** 147 172 * This is a simple array with numbers ··· 311 336 '500 foo.bar', 312 337 '600 foo&bar' 313 338 ])), 314 - bool: z.optional(z.unknown()) 339 + bool: z.optional(z.literal(true)) 315 340 }); 316 341 317 342 /** ··· 752 777 String: z.optional(z.enum([ 753 778 'String' 754 779 ])), 755 - number: z.optional(z.unknown()), 780 + number: z.optional(z.literal(0)), 756 781 null: z.optional(z.unknown()), 757 782 withType: z.optional(z.enum([ 758 783 'Some string' ··· 938 963 ]); 939 964 940 965 export const zModelWithNumericEnumUnion = z.object({ 941 - value: z.optional(z.unknown()) 966 + value: z.optional(z.union([ 967 + z.literal(-10), 968 + z.literal(-1), 969 + z.literal(0), 970 + z.literal(1), 971 + z.literal(3), 972 + z.literal(6), 973 + z.literal(12) 974 + ])) 942 975 }); 943 976 944 977 /**
+43 -10
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts
··· 125 125 'Non-ascii: øæåôöØÆÅÔÖ字符串' 126 126 ]); 127 127 128 - export const zEnumWithReplacedCharacters = z.enum([ 129 - "'Single Quote'", 130 - '"Double Quotes"', 131 - 'øæåôöØÆÅÔÖ字符串', 132 - '' 128 + export const zEnumWithReplacedCharacters = z.union([ 129 + z.literal("'Single Quote'"), 130 + z.literal('"Double Quotes"'), 131 + z.literal('øæåôöØÆÅÔÖ字符串'), 132 + z.literal(3.1), 133 + z.literal('') 133 134 ]); 134 135 135 136 /** 136 137 * This is a simple enum with numbers 137 138 */ 138 - export const zEnumWithNumbers = z.unknown(); 139 + export const zEnumWithNumbers = z.union([ 140 + z.literal(1), 141 + z.literal(2), 142 + z.literal(3), 143 + z.literal(1.1), 144 + z.literal(1.2), 145 + z.literal(1.3), 146 + z.literal(100), 147 + z.literal(200), 148 + z.literal(300), 149 + z.literal(-100), 150 + z.literal(-200), 151 + z.literal(-300), 152 + z.literal(-1.1), 153 + z.literal(-1.2), 154 + z.literal(-1.3) 155 + ]); 139 156 140 157 /** 141 158 * Success=1,Warning=2,Error=3 ··· 145 162 /** 146 163 * This is a simple enum with numbers 147 164 */ 148 - export const zEnumWithExtensions = z.unknown(); 165 + export const zEnumWithExtensions = z.union([ 166 + z.literal(200), 167 + z.literal(400), 168 + z.literal(500) 169 + ]); 149 170 150 - export const zEnumWithXEnumNames = z.unknown(); 171 + export const zEnumWithXEnumNames = z.union([ 172 + z.literal(0), 173 + z.literal(1), 174 + z.literal(2) 175 + ]); 151 176 152 177 /** 153 178 * This is a simple array with numbers ··· 320 345 '500 foo.bar', 321 346 '600 foo&bar' 322 347 ])), 323 - bool: z.optional(z.unknown()) 348 + bool: z.optional(z.literal(true)) 324 349 }); 325 350 326 351 /** ··· 944 969 ]); 945 970 946 971 export const zModelWithNumericEnumUnion = z.object({ 947 - value: z.optional(z.unknown()) 972 + value: z.optional(z.union([ 973 + z.literal(-10), 974 + z.literal(-1), 975 + z.literal(0), 976 + z.literal(1), 977 + z.literal(3), 978 + z.literal(6), 979 + z.literal(12) 980 + ])) 948 981 }); 949 982 950 983 /**
+43 -10
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts
··· 125 125 'Non-ascii: øæåôöØÆÅÔÖ字符串' 126 126 ]); 127 127 128 - export const zEnumWithReplacedCharacters = z.enum([ 129 - "'Single Quote'", 130 - '"Double Quotes"', 131 - 'øæåôöØÆÅÔÖ字符串', 132 - '' 128 + export const zEnumWithReplacedCharacters = z.union([ 129 + z.literal("'Single Quote'"), 130 + z.literal('"Double Quotes"'), 131 + z.literal('øæåôöØÆÅÔÖ字符串'), 132 + z.literal(3.1), 133 + z.literal('') 133 134 ]); 134 135 135 136 /** 136 137 * This is a simple enum with numbers 137 138 */ 138 - export const zEnumWithNumbers = z.unknown(); 139 + export const zEnumWithNumbers = z.union([ 140 + z.literal(1), 141 + z.literal(2), 142 + z.literal(3), 143 + z.literal(1.1), 144 + z.literal(1.2), 145 + z.literal(1.3), 146 + z.literal(100), 147 + z.literal(200), 148 + z.literal(300), 149 + z.literal(-100), 150 + z.literal(-200), 151 + z.literal(-300), 152 + z.literal(-1.1), 153 + z.literal(-1.2), 154 + z.literal(-1.3) 155 + ]); 139 156 140 157 /** 141 158 * Success=1,Warning=2,Error=3 ··· 145 162 /** 146 163 * This is a simple enum with numbers 147 164 */ 148 - export const zEnumWithExtensions = z.unknown(); 165 + export const zEnumWithExtensions = z.union([ 166 + z.literal(200), 167 + z.literal(400), 168 + z.literal(500) 169 + ]); 149 170 150 - export const zEnumWithXEnumNames = z.unknown(); 171 + export const zEnumWithXEnumNames = z.union([ 172 + z.literal(0), 173 + z.literal(1), 174 + z.literal(2) 175 + ]); 151 176 152 177 /** 153 178 * This is a simple array with numbers ··· 320 345 '500 foo.bar', 321 346 '600 foo&bar' 322 347 ]).optional(), 323 - bool: z.unknown().optional() 348 + bool: z.literal(true).optional() 324 349 }); 325 350 326 351 /** ··· 942 967 ]); 943 968 944 969 export const zModelWithNumericEnumUnion = z.object({ 945 - value: z.unknown().optional() 970 + value: z.union([ 971 + z.literal(-10), 972 + z.literal(-1), 973 + z.literal(0), 974 + z.literal(1), 975 + z.literal(3), 976 + z.literal(6), 977 + z.literal(12) 978 + ]).optional() 946 979 }); 947 980 948 981 /**
+43 -10
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts
··· 125 125 'Non-ascii: øæåôöØÆÅÔÖ字符串' 126 126 ]); 127 127 128 - export const zEnumWithReplacedCharacters = z.enum([ 129 - "'Single Quote'", 130 - '"Double Quotes"', 131 - 'øæåôöØÆÅÔÖ字符串', 132 - '' 128 + export const zEnumWithReplacedCharacters = z.union([ 129 + z.literal("'Single Quote'"), 130 + z.literal('"Double Quotes"'), 131 + z.literal('øæåôöØÆÅÔÖ字符串'), 132 + z.literal(3.1), 133 + z.literal('') 133 134 ]); 134 135 135 136 /** 136 137 * This is a simple enum with numbers 137 138 */ 138 - export const zEnumWithNumbers = z.unknown(); 139 + export const zEnumWithNumbers = z.union([ 140 + z.literal(1), 141 + z.literal(2), 142 + z.literal(3), 143 + z.literal(1.1), 144 + z.literal(1.2), 145 + z.literal(1.3), 146 + z.literal(100), 147 + z.literal(200), 148 + z.literal(300), 149 + z.literal(-100), 150 + z.literal(-200), 151 + z.literal(-300), 152 + z.literal(-1.1), 153 + z.literal(-1.2), 154 + z.literal(-1.3) 155 + ]); 139 156 140 157 /** 141 158 * Success=1,Warning=2,Error=3 ··· 145 162 /** 146 163 * This is a simple enum with numbers 147 164 */ 148 - export const zEnumWithExtensions = z.unknown(); 165 + export const zEnumWithExtensions = z.union([ 166 + z.literal(200), 167 + z.literal(400), 168 + z.literal(500) 169 + ]); 149 170 150 - export const zEnumWithXEnumNames = z.unknown(); 171 + export const zEnumWithXEnumNames = z.union([ 172 + z.literal(0), 173 + z.literal(1), 174 + z.literal(2) 175 + ]); 151 176 152 177 /** 153 178 * This is a simple array with numbers ··· 320 345 '500 foo.bar', 321 346 '600 foo&bar' 322 347 ])), 323 - bool: z.optional(z.unknown()) 348 + bool: z.optional(z.literal(true)) 324 349 }); 325 350 326 351 /** ··· 944 969 ]); 945 970 946 971 export const zModelWithNumericEnumUnion = z.object({ 947 - value: z.optional(z.unknown()) 972 + value: z.optional(z.union([ 973 + z.literal(-10), 974 + z.literal(-1), 975 + z.literal(0), 976 + z.literal(1), 977 + z.literal(3), 978 + z.literal(6), 979 + z.literal(12) 980 + ])) 948 981 }); 949 982 950 983 /**
+23 -3
packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts
··· 103 103 /** 104 104 * This is a simple enum with numbers 105 105 */ 106 - export const zEnumWithNumbers = z.unknown(); 106 + export const zEnumWithNumbers = z.union([ 107 + z.literal(1), 108 + z.literal(2), 109 + z.literal(3), 110 + z.literal(1.1), 111 + z.literal(1.2), 112 + z.literal(1.3), 113 + z.literal(100), 114 + z.literal(200), 115 + z.literal(300), 116 + z.literal(-100), 117 + z.literal(-200), 118 + z.literal(-300), 119 + z.literal(-1.1), 120 + z.literal(-1.2), 121 + z.literal(-1.3) 122 + ]); 107 123 108 124 /** 109 125 * Success=1,Warning=2,Error=3 ··· 113 129 /** 114 130 * This is a simple enum with numbers 115 131 */ 116 - export const zEnumWithExtensions = z.unknown(); 132 + export const zEnumWithExtensions = z.union([ 133 + z.literal(200), 134 + z.literal(400), 135 + z.literal(500) 136 + ]); 117 137 118 138 /** 119 139 * This is a simple array with numbers ··· 234 254 '500 foo.bar', 235 255 '600 foo&bar' 236 256 ])), 237 - bool: z.optional(z.unknown()) 257 + bool: z.optional(z.literal(true)) 238 258 }); 239 259 240 260 /**
+23 -3
packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts
··· 103 103 /** 104 104 * This is a simple enum with numbers 105 105 */ 106 - export const zEnumWithNumbers = z.unknown(); 106 + export const zEnumWithNumbers = z.union([ 107 + z.literal(1), 108 + z.literal(2), 109 + z.literal(3), 110 + z.literal(1.1), 111 + z.literal(1.2), 112 + z.literal(1.3), 113 + z.literal(100), 114 + z.literal(200), 115 + z.literal(300), 116 + z.literal(-100), 117 + z.literal(-200), 118 + z.literal(-300), 119 + z.literal(-1.1), 120 + z.literal(-1.2), 121 + z.literal(-1.3) 122 + ]); 107 123 108 124 /** 109 125 * Success=1,Warning=2,Error=3 ··· 113 129 /** 114 130 * This is a simple enum with numbers 115 131 */ 116 - export const zEnumWithExtensions = z.unknown(); 132 + export const zEnumWithExtensions = z.union([ 133 + z.literal(200), 134 + z.literal(400), 135 + z.literal(500) 136 + ]); 117 137 118 138 /** 119 139 * This is a simple array with numbers ··· 234 254 '500 foo.bar', 235 255 '600 foo&bar' 236 256 ]).optional(), 237 - bool: z.unknown().optional() 257 + bool: z.literal(true).optional() 238 258 }); 239 259 240 260 /**
+23 -3
packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts
··· 103 103 /** 104 104 * This is a simple enum with numbers 105 105 */ 106 - export const zEnumWithNumbers = z.unknown(); 106 + export const zEnumWithNumbers = z.union([ 107 + z.literal(1), 108 + z.literal(2), 109 + z.literal(3), 110 + z.literal(1.1), 111 + z.literal(1.2), 112 + z.literal(1.3), 113 + z.literal(100), 114 + z.literal(200), 115 + z.literal(300), 116 + z.literal(-100), 117 + z.literal(-200), 118 + z.literal(-300), 119 + z.literal(-1.1), 120 + z.literal(-1.2), 121 + z.literal(-1.3) 122 + ]); 107 123 108 124 /** 109 125 * Success=1,Warning=2,Error=3 ··· 113 129 /** 114 130 * This is a simple enum with numbers 115 131 */ 116 - export const zEnumWithExtensions = z.unknown(); 132 + export const zEnumWithExtensions = z.union([ 133 + z.literal(200), 134 + z.literal(400), 135 + z.literal(500) 136 + ]); 117 137 118 138 /** 119 139 * This is a simple array with numbers ··· 234 254 '500 foo.bar', 235 255 '600 foo&bar' 236 256 ])), 237 - bool: z.optional(z.unknown()) 257 + bool: z.optional(z.literal(true)) 238 258 }); 239 259 240 260 /**
+44 -11
packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts
··· 119 119 'Non-ascii: øæåôöØÆÅÔÖ字符串' 120 120 ]); 121 121 122 - export const zEnumWithReplacedCharacters = z.enum([ 123 - "'Single Quote'", 124 - '"Double Quotes"', 125 - 'øæåôöØÆÅÔÖ字符串', 126 - '' 122 + export const zEnumWithReplacedCharacters = z.union([ 123 + z.literal("'Single Quote'"), 124 + z.literal('"Double Quotes"'), 125 + z.literal('øæåôöØÆÅÔÖ字符串'), 126 + z.literal(3.1), 127 + z.literal('') 127 128 ]); 128 129 129 130 /** 130 131 * This is a simple enum with numbers 131 132 */ 132 - export const zEnumWithNumbers = z.unknown(); 133 + export const zEnumWithNumbers = z.union([ 134 + z.literal(1), 135 + z.literal(2), 136 + z.literal(3), 137 + z.literal(1.1), 138 + z.literal(1.2), 139 + z.literal(1.3), 140 + z.literal(100), 141 + z.literal(200), 142 + z.literal(300), 143 + z.literal(-100), 144 + z.literal(-200), 145 + z.literal(-300), 146 + z.literal(-1.1), 147 + z.literal(-1.2), 148 + z.literal(-1.3) 149 + ]); 133 150 134 151 /** 135 152 * Success=1,Warning=2,Error=3 ··· 139 156 /** 140 157 * This is a simple enum with numbers 141 158 */ 142 - export const zEnumWithExtensions = z.unknown(); 159 + export const zEnumWithExtensions = z.union([ 160 + z.literal(200), 161 + z.literal(400), 162 + z.literal(500) 163 + ]); 143 164 144 - export const zEnumWithXEnumNames = z.unknown(); 165 + export const zEnumWithXEnumNames = z.union([ 166 + z.literal(0), 167 + z.literal(1), 168 + z.literal(2) 169 + ]); 145 170 146 171 /** 147 172 * This is a simple array with numbers ··· 311 336 '500 foo.bar', 312 337 '600 foo&bar' 313 338 ])), 314 - bool: z.optional(z.unknown()) 339 + bool: z.optional(z.literal(true)) 315 340 }); 316 341 317 342 /** ··· 752 777 String: z.optional(z.enum([ 753 778 'String' 754 779 ])), 755 - number: z.optional(z.unknown()), 780 + number: z.optional(z.literal(0)), 756 781 null: z.optional(z.unknown()), 757 782 withType: z.optional(z.enum([ 758 783 'Some string' ··· 938 963 ]); 939 964 940 965 export const zModelWithNumericEnumUnion = z.object({ 941 - value: z.optional(z.unknown()) 966 + value: z.optional(z.union([ 967 + z.literal(-10), 968 + z.literal(-1), 969 + z.literal(0), 970 + z.literal(1), 971 + z.literal(3), 972 + z.literal(6), 973 + z.literal(12) 974 + ])) 942 975 }); 943 976 944 977 /**
+44 -11
packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts
··· 119 119 'Non-ascii: øæåôöØÆÅÔÖ字符串' 120 120 ]); 121 121 122 - export const zEnumWithReplacedCharacters = z.enum([ 123 - "'Single Quote'", 124 - '"Double Quotes"', 125 - 'øæåôöØÆÅÔÖ字符串', 126 - '' 122 + export const zEnumWithReplacedCharacters = z.union([ 123 + z.literal("'Single Quote'"), 124 + z.literal('"Double Quotes"'), 125 + z.literal('øæåôöØÆÅÔÖ字符串'), 126 + z.literal(3.1), 127 + z.literal('') 127 128 ]); 128 129 129 130 /** 130 131 * This is a simple enum with numbers 131 132 */ 132 - export const zEnumWithNumbers = z.unknown(); 133 + export const zEnumWithNumbers = z.union([ 134 + z.literal(1), 135 + z.literal(2), 136 + z.literal(3), 137 + z.literal(1.1), 138 + z.literal(1.2), 139 + z.literal(1.3), 140 + z.literal(100), 141 + z.literal(200), 142 + z.literal(300), 143 + z.literal(-100), 144 + z.literal(-200), 145 + z.literal(-300), 146 + z.literal(-1.1), 147 + z.literal(-1.2), 148 + z.literal(-1.3) 149 + ]); 133 150 134 151 /** 135 152 * Success=1,Warning=2,Error=3 ··· 139 156 /** 140 157 * This is a simple enum with numbers 141 158 */ 142 - export const zEnumWithExtensions = z.unknown(); 159 + export const zEnumWithExtensions = z.union([ 160 + z.literal(200), 161 + z.literal(400), 162 + z.literal(500) 163 + ]); 143 164 144 - export const zEnumWithXEnumNames = z.unknown(); 165 + export const zEnumWithXEnumNames = z.union([ 166 + z.literal(0), 167 + z.literal(1), 168 + z.literal(2) 169 + ]); 145 170 146 171 /** 147 172 * This is a simple array with numbers ··· 311 336 '500 foo.bar', 312 337 '600 foo&bar' 313 338 ]).optional(), 314 - bool: z.unknown().optional() 339 + bool: z.literal(true).optional() 315 340 }); 316 341 317 342 /** ··· 750 775 String: z.enum([ 751 776 'String' 752 777 ]).optional(), 753 - number: z.unknown().optional(), 778 + number: z.literal(0).optional(), 754 779 null: z.unknown().optional(), 755 780 withType: z.enum([ 756 781 'Some string' ··· 936 961 ]); 937 962 938 963 export const zModelWithNumericEnumUnion = z.object({ 939 - value: z.unknown().optional() 964 + value: z.union([ 965 + z.literal(-10), 966 + z.literal(-1), 967 + z.literal(0), 968 + z.literal(1), 969 + z.literal(3), 970 + z.literal(6), 971 + z.literal(12) 972 + ]).optional() 940 973 }); 941 974 942 975 /**
+44 -11
packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts
··· 119 119 'Non-ascii: øæåôöØÆÅÔÖ字符串' 120 120 ]); 121 121 122 - export const zEnumWithReplacedCharacters = z.enum([ 123 - "'Single Quote'", 124 - '"Double Quotes"', 125 - 'øæåôöØÆÅÔÖ字符串', 126 - '' 122 + export const zEnumWithReplacedCharacters = z.union([ 123 + z.literal("'Single Quote'"), 124 + z.literal('"Double Quotes"'), 125 + z.literal('øæåôöØÆÅÔÖ字符串'), 126 + z.literal(3.1), 127 + z.literal('') 127 128 ]); 128 129 129 130 /** 130 131 * This is a simple enum with numbers 131 132 */ 132 - export const zEnumWithNumbers = z.unknown(); 133 + export const zEnumWithNumbers = z.union([ 134 + z.literal(1), 135 + z.literal(2), 136 + z.literal(3), 137 + z.literal(1.1), 138 + z.literal(1.2), 139 + z.literal(1.3), 140 + z.literal(100), 141 + z.literal(200), 142 + z.literal(300), 143 + z.literal(-100), 144 + z.literal(-200), 145 + z.literal(-300), 146 + z.literal(-1.1), 147 + z.literal(-1.2), 148 + z.literal(-1.3) 149 + ]); 133 150 134 151 /** 135 152 * Success=1,Warning=2,Error=3 ··· 139 156 /** 140 157 * This is a simple enum with numbers 141 158 */ 142 - export const zEnumWithExtensions = z.unknown(); 159 + export const zEnumWithExtensions = z.union([ 160 + z.literal(200), 161 + z.literal(400), 162 + z.literal(500) 163 + ]); 143 164 144 - export const zEnumWithXEnumNames = z.unknown(); 165 + export const zEnumWithXEnumNames = z.union([ 166 + z.literal(0), 167 + z.literal(1), 168 + z.literal(2) 169 + ]); 145 170 146 171 /** 147 172 * This is a simple array with numbers ··· 311 336 '500 foo.bar', 312 337 '600 foo&bar' 313 338 ])), 314 - bool: z.optional(z.unknown()) 339 + bool: z.optional(z.literal(true)) 315 340 }); 316 341 317 342 /** ··· 752 777 String: z.optional(z.enum([ 753 778 'String' 754 779 ])), 755 - number: z.optional(z.unknown()), 780 + number: z.optional(z.literal(0)), 756 781 null: z.optional(z.unknown()), 757 782 withType: z.optional(z.enum([ 758 783 'Some string' ··· 938 963 ]); 939 964 940 965 export const zModelWithNumericEnumUnion = z.object({ 941 - value: z.optional(z.unknown()) 966 + value: z.optional(z.union([ 967 + z.literal(-10), 968 + z.literal(-1), 969 + z.literal(0), 970 + z.literal(1), 971 + z.literal(3), 972 + z.literal(6), 973 + z.literal(12) 974 + ])) 942 975 }); 943 976 944 977 /**
+43 -10
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts
··· 125 125 'Non-ascii: øæåôöØÆÅÔÖ字符串' 126 126 ]); 127 127 128 - export const zEnumWithReplacedCharacters = z.enum([ 129 - "'Single Quote'", 130 - '"Double Quotes"', 131 - 'øæåôöØÆÅÔÖ字符串', 132 - '' 128 + export const zEnumWithReplacedCharacters = z.union([ 129 + z.literal("'Single Quote'"), 130 + z.literal('"Double Quotes"'), 131 + z.literal('øæåôöØÆÅÔÖ字符串'), 132 + z.literal(3.1), 133 + z.literal('') 133 134 ]); 134 135 135 136 /** 136 137 * This is a simple enum with numbers 137 138 */ 138 - export const zEnumWithNumbers = z.unknown(); 139 + export const zEnumWithNumbers = z.union([ 140 + z.literal(1), 141 + z.literal(2), 142 + z.literal(3), 143 + z.literal(1.1), 144 + z.literal(1.2), 145 + z.literal(1.3), 146 + z.literal(100), 147 + z.literal(200), 148 + z.literal(300), 149 + z.literal(-100), 150 + z.literal(-200), 151 + z.literal(-300), 152 + z.literal(-1.1), 153 + z.literal(-1.2), 154 + z.literal(-1.3) 155 + ]); 139 156 140 157 /** 141 158 * Success=1,Warning=2,Error=3 ··· 145 162 /** 146 163 * This is a simple enum with numbers 147 164 */ 148 - export const zEnumWithExtensions = z.unknown(); 165 + export const zEnumWithExtensions = z.union([ 166 + z.literal(200), 167 + z.literal(400), 168 + z.literal(500) 169 + ]); 149 170 150 - export const zEnumWithXEnumNames = z.unknown(); 171 + export const zEnumWithXEnumNames = z.union([ 172 + z.literal(0), 173 + z.literal(1), 174 + z.literal(2) 175 + ]); 151 176 152 177 /** 153 178 * This is a simple array with numbers ··· 320 345 '500 foo.bar', 321 346 '600 foo&bar' 322 347 ])), 323 - bool: z.optional(z.unknown()) 348 + bool: z.optional(z.literal(true)) 324 349 }); 325 350 326 351 /** ··· 944 969 ]); 945 970 946 971 export const zModelWithNumericEnumUnion = z.object({ 947 - value: z.optional(z.unknown()) 972 + value: z.optional(z.union([ 973 + z.literal(-10), 974 + z.literal(-1), 975 + z.literal(0), 976 + z.literal(1), 977 + z.literal(3), 978 + z.literal(6), 979 + z.literal(12) 980 + ])) 948 981 }); 949 982 950 983 /**
+43 -10
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts
··· 125 125 'Non-ascii: øæåôöØÆÅÔÖ字符串' 126 126 ]); 127 127 128 - export const zEnumWithReplacedCharacters = z.enum([ 129 - "'Single Quote'", 130 - '"Double Quotes"', 131 - 'øæåôöØÆÅÔÖ字符串', 132 - '' 128 + export const zEnumWithReplacedCharacters = z.union([ 129 + z.literal("'Single Quote'"), 130 + z.literal('"Double Quotes"'), 131 + z.literal('øæåôöØÆÅÔÖ字符串'), 132 + z.literal(3.1), 133 + z.literal('') 133 134 ]); 134 135 135 136 /** 136 137 * This is a simple enum with numbers 137 138 */ 138 - export const zEnumWithNumbers = z.unknown(); 139 + export const zEnumWithNumbers = z.union([ 140 + z.literal(1), 141 + z.literal(2), 142 + z.literal(3), 143 + z.literal(1.1), 144 + z.literal(1.2), 145 + z.literal(1.3), 146 + z.literal(100), 147 + z.literal(200), 148 + z.literal(300), 149 + z.literal(-100), 150 + z.literal(-200), 151 + z.literal(-300), 152 + z.literal(-1.1), 153 + z.literal(-1.2), 154 + z.literal(-1.3) 155 + ]); 139 156 140 157 /** 141 158 * Success=1,Warning=2,Error=3 ··· 145 162 /** 146 163 * This is a simple enum with numbers 147 164 */ 148 - export const zEnumWithExtensions = z.unknown(); 165 + export const zEnumWithExtensions = z.union([ 166 + z.literal(200), 167 + z.literal(400), 168 + z.literal(500) 169 + ]); 149 170 150 - export const zEnumWithXEnumNames = z.unknown(); 171 + export const zEnumWithXEnumNames = z.union([ 172 + z.literal(0), 173 + z.literal(1), 174 + z.literal(2) 175 + ]); 151 176 152 177 /** 153 178 * This is a simple array with numbers ··· 320 345 '500 foo.bar', 321 346 '600 foo&bar' 322 347 ]).optional(), 323 - bool: z.unknown().optional() 348 + bool: z.literal(true).optional() 324 349 }); 325 350 326 351 /** ··· 942 967 ]); 943 968 944 969 export const zModelWithNumericEnumUnion = z.object({ 945 - value: z.unknown().optional() 970 + value: z.union([ 971 + z.literal(-10), 972 + z.literal(-1), 973 + z.literal(0), 974 + z.literal(1), 975 + z.literal(3), 976 + z.literal(6), 977 + z.literal(12) 978 + ]).optional() 946 979 }); 947 980 948 981 /**
+43 -10
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts
··· 125 125 'Non-ascii: øæåôöØÆÅÔÖ字符串' 126 126 ]); 127 127 128 - export const zEnumWithReplacedCharacters = z.enum([ 129 - "'Single Quote'", 130 - '"Double Quotes"', 131 - 'øæåôöØÆÅÔÖ字符串', 132 - '' 128 + export const zEnumWithReplacedCharacters = z.union([ 129 + z.literal("'Single Quote'"), 130 + z.literal('"Double Quotes"'), 131 + z.literal('øæåôöØÆÅÔÖ字符串'), 132 + z.literal(3.1), 133 + z.literal('') 133 134 ]); 134 135 135 136 /** 136 137 * This is a simple enum with numbers 137 138 */ 138 - export const zEnumWithNumbers = z.unknown(); 139 + export const zEnumWithNumbers = z.union([ 140 + z.literal(1), 141 + z.literal(2), 142 + z.literal(3), 143 + z.literal(1.1), 144 + z.literal(1.2), 145 + z.literal(1.3), 146 + z.literal(100), 147 + z.literal(200), 148 + z.literal(300), 149 + z.literal(-100), 150 + z.literal(-200), 151 + z.literal(-300), 152 + z.literal(-1.1), 153 + z.literal(-1.2), 154 + z.literal(-1.3) 155 + ]); 139 156 140 157 /** 141 158 * Success=1,Warning=2,Error=3 ··· 145 162 /** 146 163 * This is a simple enum with numbers 147 164 */ 148 - export const zEnumWithExtensions = z.unknown(); 165 + export const zEnumWithExtensions = z.union([ 166 + z.literal(200), 167 + z.literal(400), 168 + z.literal(500) 169 + ]); 149 170 150 - export const zEnumWithXEnumNames = z.unknown(); 171 + export const zEnumWithXEnumNames = z.union([ 172 + z.literal(0), 173 + z.literal(1), 174 + z.literal(2) 175 + ]); 151 176 152 177 /** 153 178 * This is a simple array with numbers ··· 320 345 '500 foo.bar', 321 346 '600 foo&bar' 322 347 ])), 323 - bool: z.optional(z.unknown()) 348 + bool: z.optional(z.literal(true)) 324 349 }); 325 350 326 351 /** ··· 944 969 ]); 945 970 946 971 export const zModelWithNumericEnumUnion = z.object({ 947 - value: z.optional(z.unknown()) 972 + value: z.optional(z.union([ 973 + z.literal(-10), 974 + z.literal(-1), 975 + z.literal(0), 976 + z.literal(1), 977 + z.literal(3), 978 + z.literal(6), 979 + z.literal(12) 980 + ])) 948 981 }); 949 982 950 983 /**
+72 -16
packages/openapi-ts/src/plugins/zod/mini/plugin.ts
··· 195 195 const result: Partial<Omit<ZodSchema, 'typeName'>> = {}; 196 196 197 197 const enumMembers: Array<ts.LiteralExpression> = []; 198 + const literalMembers: Array<ts.CallExpression> = []; 198 199 199 200 let isNullable = false; 201 + let allStrings = true; 200 202 201 203 for (const item of schema.items ?? []) { 202 - // Zod supports only string enums 204 + // Zod supports string, number, and boolean enums 203 205 if (item.type === 'string' && typeof item.const === 'string') { 204 - enumMembers.push( 205 - tsc.stringLiteral({ 206 - text: item.const, 206 + const stringLiteral = tsc.stringLiteral({ 207 + text: item.const, 208 + }); 209 + enumMembers.push(stringLiteral); 210 + literalMembers.push( 211 + tsc.callExpression({ 212 + functionName: tsc.propertyAccessExpression({ 213 + expression: zSymbol.placeholder, 214 + name: identifiers.literal, 215 + }), 216 + parameters: [stringLiteral], 217 + }), 218 + ); 219 + } else if ( 220 + (item.type === 'number' || item.type === 'integer') && 221 + typeof item.const === 'number' 222 + ) { 223 + allStrings = false; 224 + const numberLiteral = tsc.ots.number(item.const); 225 + literalMembers.push( 226 + tsc.callExpression({ 227 + functionName: tsc.propertyAccessExpression({ 228 + expression: zSymbol.placeholder, 229 + name: identifiers.literal, 230 + }), 231 + parameters: [numberLiteral], 232 + }), 233 + ); 234 + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 235 + allStrings = false; 236 + const booleanLiteral = tsc.ots.boolean(item.const); 237 + literalMembers.push( 238 + tsc.callExpression({ 239 + functionName: tsc.propertyAccessExpression({ 240 + expression: zSymbol.placeholder, 241 + name: identifiers.literal, 242 + }), 243 + parameters: [booleanLiteral], 207 244 }), 208 245 ); 209 246 } else if (item.type === 'null' || item.const === null) { ··· 211 248 } 212 249 } 213 250 214 - if (!enumMembers.length) { 251 + if (!literalMembers.length) { 215 252 return unknownTypeToZodSchema({ 216 253 plugin, 217 254 schema: { ··· 220 257 }); 221 258 } 222 259 223 - result.expression = tsc.callExpression({ 224 - functionName: tsc.propertyAccessExpression({ 225 - expression: zSymbol.placeholder, 226 - name: identifiers.enum, 227 - }), 228 - parameters: [ 229 - tsc.arrayLiteralExpression({ 230 - elements: enumMembers, 231 - multiLine: false, 260 + // Use z.enum() for pure string enums, z.union() for mixed or non-string types 261 + if (allStrings && enumMembers.length > 0) { 262 + result.expression = tsc.callExpression({ 263 + functionName: tsc.propertyAccessExpression({ 264 + expression: zSymbol.placeholder, 265 + name: identifiers.enum, 232 266 }), 233 - ], 234 - }); 267 + parameters: [ 268 + tsc.arrayLiteralExpression({ 269 + elements: enumMembers, 270 + multiLine: false, 271 + }), 272 + ], 273 + }); 274 + } else if (literalMembers.length === 1) { 275 + // For single-member unions, use the member directly instead of wrapping in z.union() 276 + result.expression = literalMembers[0]; 277 + } else { 278 + result.expression = tsc.callExpression({ 279 + functionName: tsc.propertyAccessExpression({ 280 + expression: zSymbol.placeholder, 281 + name: identifiers.union, 282 + }), 283 + parameters: [ 284 + tsc.arrayLiteralExpression({ 285 + elements: literalMembers, 286 + multiLine: literalMembers.length > 3, 287 + }), 288 + ], 289 + }); 290 + } 235 291 236 292 if (isNullable) { 237 293 result.expression = tsc.callExpression({
+73 -16
packages/openapi-ts/src/plugins/zod/v3/plugin.ts
··· 179 179 ); 180 180 181 181 const enumMembers: Array<ts.LiteralExpression> = []; 182 + const literalMembers: Array<ts.CallExpression> = []; 182 183 183 184 let isNullable = false; 185 + let allStrings = true; 184 186 185 187 for (const item of schema.items ?? []) { 186 - // Zod supports only string enums 188 + // Zod supports string, number, and boolean enums 187 189 if (item.type === 'string' && typeof item.const === 'string') { 188 - enumMembers.push( 189 - tsc.stringLiteral({ 190 - text: item.const, 190 + const stringLiteral = tsc.stringLiteral({ 191 + text: item.const, 192 + }); 193 + enumMembers.push(stringLiteral); 194 + literalMembers.push( 195 + tsc.callExpression({ 196 + functionName: tsc.propertyAccessExpression({ 197 + expression: zSymbol.placeholder, 198 + name: identifiers.literal, 199 + }), 200 + parameters: [stringLiteral], 201 + }), 202 + ); 203 + } else if ( 204 + (item.type === 'number' || item.type === 'integer') && 205 + typeof item.const === 'number' 206 + ) { 207 + allStrings = false; 208 + const numberLiteral = tsc.ots.number(item.const); 209 + literalMembers.push( 210 + tsc.callExpression({ 211 + functionName: tsc.propertyAccessExpression({ 212 + expression: zSymbol.placeholder, 213 + name: identifiers.literal, 214 + }), 215 + parameters: [numberLiteral], 216 + }), 217 + ); 218 + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 219 + allStrings = false; 220 + const booleanLiteral = tsc.ots.boolean(item.const); 221 + literalMembers.push( 222 + tsc.callExpression({ 223 + functionName: tsc.propertyAccessExpression({ 224 + expression: zSymbol.placeholder, 225 + name: identifiers.literal, 226 + }), 227 + parameters: [booleanLiteral], 191 228 }), 192 229 ); 193 230 } else if (item.type === 'null' || item.const === null) { ··· 195 232 } 196 233 } 197 234 198 - if (!enumMembers.length) { 235 + if (!literalMembers.length) { 199 236 return unknownTypeToZodSchema({ 200 237 plugin, 201 238 schema: { ··· 204 241 }); 205 242 } 206 243 207 - let enumExpression = tsc.callExpression({ 208 - functionName: tsc.propertyAccessExpression({ 209 - expression: zSymbol.placeholder, 210 - name: identifiers.enum, 211 - }), 212 - parameters: [ 213 - tsc.arrayLiteralExpression({ 214 - elements: enumMembers, 215 - multiLine: false, 244 + // Use z.enum() for pure string enums, z.union() for mixed or non-string types 245 + let enumExpression: ts.CallExpression; 246 + if (allStrings && enumMembers.length > 0) { 247 + enumExpression = tsc.callExpression({ 248 + functionName: tsc.propertyAccessExpression({ 249 + expression: zSymbol.placeholder, 250 + name: identifiers.enum, 216 251 }), 217 - ], 218 - }); 252 + parameters: [ 253 + tsc.arrayLiteralExpression({ 254 + elements: enumMembers, 255 + multiLine: false, 256 + }), 257 + ], 258 + }); 259 + } else if (literalMembers.length === 1) { 260 + // For single-member unions, use the member directly instead of wrapping in z.union() 261 + enumExpression = literalMembers[0]!; 262 + } else { 263 + enumExpression = tsc.callExpression({ 264 + functionName: tsc.propertyAccessExpression({ 265 + expression: zSymbol.placeholder, 266 + name: identifiers.union, 267 + }), 268 + parameters: [ 269 + tsc.arrayLiteralExpression({ 270 + elements: literalMembers, 271 + multiLine: literalMembers.length > 3, 272 + }), 273 + ], 274 + }); 275 + } 219 276 220 277 if (isNullable) { 221 278 enumExpression = tsc.callExpression({
+76 -20
packages/openapi-ts/src/plugins/zod/v4/plugin.ts
··· 172 172 }): Omit<ZodSchema, 'typeName'> => { 173 173 const result: Partial<Omit<ZodSchema, 'typeName'>> = {}; 174 174 175 + const zSymbol = plugin.referenceSymbol( 176 + plugin.api.getSelector('import', 'zod'), 177 + ); 178 + 175 179 const enumMembers: Array<ts.LiteralExpression> = []; 180 + const literalMembers: Array<ts.CallExpression> = []; 176 181 177 182 let isNullable = false; 183 + let allStrings = true; 178 184 179 185 for (const item of schema.items ?? []) { 180 - // Zod supports only string enums 186 + // Zod supports string, number, and boolean enums 181 187 if (item.type === 'string' && typeof item.const === 'string') { 182 - enumMembers.push( 183 - tsc.stringLiteral({ 184 - text: item.const, 188 + const stringLiteral = tsc.stringLiteral({ 189 + text: item.const, 190 + }); 191 + enumMembers.push(stringLiteral); 192 + literalMembers.push( 193 + tsc.callExpression({ 194 + functionName: tsc.propertyAccessExpression({ 195 + expression: zSymbol.placeholder, 196 + name: identifiers.literal, 197 + }), 198 + parameters: [stringLiteral], 199 + }), 200 + ); 201 + } else if ( 202 + (item.type === 'number' || item.type === 'integer') && 203 + typeof item.const === 'number' 204 + ) { 205 + allStrings = false; 206 + const numberLiteral = tsc.ots.number(item.const); 207 + literalMembers.push( 208 + tsc.callExpression({ 209 + functionName: tsc.propertyAccessExpression({ 210 + expression: zSymbol.placeholder, 211 + name: identifiers.literal, 212 + }), 213 + parameters: [numberLiteral], 214 + }), 215 + ); 216 + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 217 + allStrings = false; 218 + const booleanLiteral = tsc.ots.boolean(item.const); 219 + literalMembers.push( 220 + tsc.callExpression({ 221 + functionName: tsc.propertyAccessExpression({ 222 + expression: zSymbol.placeholder, 223 + name: identifiers.literal, 224 + }), 225 + parameters: [booleanLiteral], 185 226 }), 186 227 ); 187 228 } else if (item.type === 'null' || item.const === null) { ··· 189 230 } 190 231 } 191 232 192 - if (!enumMembers.length) { 233 + if (!literalMembers.length) { 193 234 return unknownTypeToZodSchema({ 194 235 plugin, 195 236 schema: { ··· 198 239 }); 199 240 } 200 241 201 - const zSymbol = plugin.referenceSymbol( 202 - plugin.api.getSelector('import', 'zod'), 203 - ); 204 - 205 - result.expression = tsc.callExpression({ 206 - functionName: tsc.propertyAccessExpression({ 207 - expression: zSymbol.placeholder, 208 - name: identifiers.enum, 209 - }), 210 - parameters: [ 211 - tsc.arrayLiteralExpression({ 212 - elements: enumMembers, 213 - multiLine: false, 242 + // Use z.enum() for pure string enums, z.union() for mixed or non-string types 243 + if (allStrings && enumMembers.length > 0) { 244 + result.expression = tsc.callExpression({ 245 + functionName: tsc.propertyAccessExpression({ 246 + expression: zSymbol.placeholder, 247 + name: identifiers.enum, 214 248 }), 215 - ], 216 - }); 249 + parameters: [ 250 + tsc.arrayLiteralExpression({ 251 + elements: enumMembers, 252 + multiLine: false, 253 + }), 254 + ], 255 + }); 256 + } else if (literalMembers.length === 1) { 257 + // For single-member unions, use the member directly instead of wrapping in z.union() 258 + result.expression = literalMembers[0]; 259 + } else { 260 + result.expression = tsc.callExpression({ 261 + functionName: tsc.propertyAccessExpression({ 262 + expression: zSymbol.placeholder, 263 + name: identifiers.union, 264 + }), 265 + parameters: [ 266 + tsc.arrayLiteralExpression({ 267 + elements: literalMembers, 268 + multiLine: literalMembers.length > 3, 269 + }), 270 + ], 271 + }); 272 + } 217 273 218 274 if (isNullable) { 219 275 result.expression = tsc.callExpression({