Bence (Arzen)
Bence (Arzen)
ZZod
Created by Bence (Arzen) on 10/16/2024 in #questions
Bence (Arzen) - Hi guys 🙂 Is there a way to cr...
Thank you Steve! I'll take a look at your examples 👍
6 replies
ZZod
Created by Bence (Arzen) on 5/15/2024 in #questions
Bence (Arzen) - Hey guys 🙂I have a (might be)...
Thank you for your insight! 🙂 It was very useful, I'll do my best to embrace what the language provides.
22 replies
ZZod
Created by Bence (Arzen) on 5/15/2024 in #questions
Bence (Arzen) - Hey guys 🙂I have a (might be)...
I had a sort of "solution" but thought there is something better that I do not know of:
const schema = z.object({
userId: z
.string({ required_error: 'User Id required') })
.min(1, 'User Id required')
.pipe(userIdInstance.schema)
})
const schema = z.object({
userId: z
.string({ required_error: 'User Id required') })
.min(1, 'User Id required')
.pipe(userIdInstance.schema)
})
I understand your point of view. My main reason why I want to enforce type safety and create reference and id types is to avoid passing in something unintended around my codebase and scratch my head why everything collapses. My issues it type aliases in TS. Consider the following code:
type UserId = number
type CustomerId = number

async function getCustomerData(customerId: CustomerId): Promise<object> {
// etc...
}

const userId: UserId = 123
const customerId: CustomerId = 456

// TS will allow this as the underlying type equivalent...
getCustomerData(userId)
type UserId = number
type CustomerId = number

async function getCustomerData(customerId: CustomerId): Promise<object> {
// etc...
}

const userId: UserId = 123
const customerId: CustomerId = 456

// TS will allow this as the underlying type equivalent...
getCustomerData(userId)
I "understand" that we only created a type alias but coming from Scala I hate this behaviour 😂
22 replies
ZZod
Created by Bence (Arzen) on 5/15/2024 in #questions
Bence (Arzen) - Hey guys 🙂I have a (might be)...
I don't think the global error map will work. I also tried to use refine and superRefine but if the example schema above (userIdSchema) failes, it will not propogate anything to the refine/superRefine block (as it is expected I believe)
22 replies
ZZod
Created by Bence (Arzen) on 5/15/2024 in #questions
Bence (Arzen) - Hey guys 🙂I have a (might be)...
@here sorry for the late reply. I didn't get any notification from the server for some reason. The schema I am trying to override the error on is indeed simple. I'll get you guys some code example how it's being created then how I try to use it later in our codebase. Schema creation example:
function createOpaqueInstance<OpaqueType extends Newtype<unknown, unknown>>(
schema: z.ZodTypeAny,
isValid: (value: OpaqueType['_A']) => boolean,
) {
const _iso = iso<OpaqueType>()
const _prism = prism<OpaqueType>(isValid)
const _schema = schema.refine(isValid).transform(_iso.wrap)

const instance: NewTypeInstance<OpaqueType['_A'], OpaqueType> = {
prism: _prism,
schema: _schema,
unwrap: _iso.unwrap,
}

return instance
}
function createOpaqueInstance<OpaqueType extends Newtype<unknown, unknown>>(
schema: z.ZodTypeAny,
isValid: (value: OpaqueType['_A']) => boolean,
) {
const _iso = iso<OpaqueType>()
const _prism = prism<OpaqueType>(isValid)
const _schema = schema.refine(isValid).transform(_iso.wrap)

const instance: NewTypeInstance<OpaqueType['_A'], OpaqueType> = {
prism: _prism,
schema: _schema,
unwrap: _iso.unwrap,
}

return instance
}
I am using a couple of helper libraries which aren't really important to include here.
function isValidId(value: number | string): boolean {
// etc...
}

export function createIdInstance<OpaqueType extends Newtype<unknown, number>>() {
return createOpaqueInstance<OpaqueType>(z.string().or(z.number()), isValidId)
}
function isValidId(value: number | string): boolean {
// etc...
}

export function createIdInstance<OpaqueType extends Newtype<unknown, number>>() {
return createOpaqueInstance<OpaqueType>(z.string().or(z.number()), isValidId)
}
Then the way I create these types is the following:
export type UserId = Newtype<{ readonly UserId: unique symbol }, number>

export const userIdInstance = createIdInstance<UserId>()
export type UserId = Newtype<{ readonly UserId: unique symbol }, number>

export const userIdInstance = createIdInstance<UserId>()
Once I have the userIdInstance I can access the schema on it and safely wrap or unwrap any values.
userIdInstance.schema // gets back the schema which is just a z.string().or(z.number())
userIdInstance.schema // gets back the schema which is just a z.string().or(z.number())
The type we end up with is the following:
type A = z.infer<typeof userIdInstance.schema>

type A = Newtype<{
readonly UserId: unique symbol;
}, number>
type A = z.infer<typeof userIdInstance.schema>

type A = Newtype<{
readonly UserId: unique symbol;
}, number>
The way I use it is the following:
export const updateUserNameSchema = z.object({
userId: userIdInstance.schema, // I'd like to override the error message here somehow
name: z
.string({ required_error: "Name is required" })
.min(1, "Name is required")
})
export const updateUserNameSchema = z.object({
userId: userIdInstance.schema, // I'd like to override the error message here somehow
name: z
.string({ required_error: "Name is required" })
.min(1, "Name is required")
})
22 replies