#Zod schema definition for enum that allows/ignores invalid enum values

1 messages · Page 1 of 1 (latest)

still pumice
#

I have a list of valid string literals in a readonly/as const array, and am constructing a zod z.enum using this. I want to keep the type checking benefits for the consumer of the zod schema (i.e. only allowing a value parsed using the schema to reference values from the enum), while ignoring/discarding unknown values during the actual parsing behind the scenes.

Playground example:
https://tsplay.dev/mLvo4w

sterile blade
#

@still pumice what about smth like Extract<EnumType, AllowedArray[number]>

still pumice
#

I dont have an actual TS/JS enum, just a readonly array of values (from which I also derive a TS union type), and a zod enum schema (which is the thing I want to change to be less strict while not loosening the return type of the parsed object)

sterile blade
#

then typeof ZodEnum

#

or whatever method that zod has

#

for extracting types

still pumice
#

I'm not after a new type though, it's a runtime issue not a type level one

sterile blade
#

yeah its z.infer

still pumice
#

I dont see how constructing a new type helps me. getting the inferred type of the z.enum would just give me back the initial type I already have (a strict union of string literals)

#

zod is a runtime validation library, so constructing a new compile time type isn't really going to help since I need the runtime validation logic to change to be less strict while keeping the same strictness in compile time type checking

sterile blade
#

ah

#

after reading the code like 10 times i finally understand what you mean

still pumice
#

I basically want it to still infer strict TS types so TS will fail to compile for incorrect values being referenced in the code, while allowing the runtime validation to not fail if unexpected values are encountered

sterile blade
#

rates: z.record(z.string() as unknown as typeof currencyCodeSchema, z.number()),

#

what about this?

#

TS will see typeof currencyCodeSchema, zod will see z.string()

still pumice
#

that's not a bad idea

#

obviously the ideal solution would be without any casting, but i might end up using that if there arent any other options

#

the behaviour of z.object is actually more in like with what I want than z.record, since by default z.object will allow extra properties unless you add a .strict to it, whereas z.record with an enum or union for the keys is by default strict

#

but constructing a z.object out of an array of keys while keeping type checking is proving difficult

#
type RatesSchema = {
    [Key in typeof currencyCodes[number]]: z.ZodNumber
}

const ratesSchema = z.object(Object.fromEntries(currencyCodes.map(x => [x, z.number()])) as RatesSchema)```
this works but also requires a cast because `Object.fromEntries` throws away the types of the `currencyCodes` array
dawn bloom
#

Would this work?

z.enum(currencyCodes).optional().catch(undefined)
#

That would allow an explicit undefined in TS, which is maybe not 100% what you'd want but seems like a small downside.

still pumice
#

unfortunately zod doesn't allow that as a valid schema for the keys of a z.record :/

#

I guess because undefined isnt a valid object key

#

this technically works because it utilises the leniency of z.object allowing extra keys but is cursed lol
https://tsplay.dev/wO5ozm

dawn bloom
#

BTW you don't need to shorten playground links there's a bot here that does that automatically.

still pumice
#

I know, but it doesn't seem to be working for me today. I tried pasting a playground link and it didnt get replaced by the bot

empty roverBOT
#
retsam19#0

Preview:```ts
import {Zodios} from "@zodios/core"
import {z} from "zod"

const currencyCodes = [
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTN",
"BWP",
"BYN",
"BZD",
"CAD",
"CDF",
"CHF",
"CLP",
"CNY",
"COP",
"CRC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EGP",
"ERN",
"ETB",
"EUR",
"FJD",
"FKP",
"FOK",
"GBP",
"GEL",
"GGP",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"IMP",
"INR",
"IQD",
"IRR",
"ISK",
"JEP",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KID",
"KMF",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRU",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLE",
"SLL",
"SOS",
"SRD",
"SSP",
"STN",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TVD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VES",
"VND",
"VUV",
"WST",
"XAF",
"XCD",
"XDR",
"XOF",
"XPF",
"YER",
"ZAR",
"ZMW",
"ZWL",
] as const
...```

dawn bloom
#

Gross, but seems to work

still pumice
#

lol

dawn bloom
#

But yeah, it seems like you'd want z.record but with the ignoring non-stripping behavior of z.object. I feel like that should be possible without going 'outside the box', but I'm not finding it.

still pumice
#

yeah I looked for a while and none of the methods that z.record provides seem to emulate that property of a non-.strict z.object

#

if only I could type-safely map the string literal array into an object while preserving the types of the keys

#

cause unfortunately Object.fromEntries<T> just returns a { [k: string]: T }