import { z } from 'zod';
function testFn<T>(zodType: z.ZodType<T>) {
return zodType;
}
const branded_1 = z.string().brand('type1');
const branded_2 = z.string().brand('type2');
const bad_record = z.record(branded_1, z.string());
const good_1 = z.object({
branded_1,
});
testFn(good_1); // No type errors, a branded_1 in an object is fine
const good_2 = z.object({
branded_2,
});
testFn(good_2); // No type errors, branded_2 in an object is fine
const good_3 = z.object({
branded_1,
branded_2,
record_1: z.record(branded_1, z.any()),
record_2: z.record(branded_1, z.unknown()),
record_3: z.record(branded_1, z.undefined()),
});
testFn(good_3); // No type errors, we can have branded records where the value is potentially undefined
const good_4 = z.object({
bad_record,
});
testFn(good_4); // No type errors, bad_record is not bad by itself
const good_5 = z.object({
x: z.number(),
bad_record,
});
testFn(good_5); // No type errors, bad_record is not bad with some other non-branded types
const bad_1 = z.object({
branded_1,
bad_record,
});
testFn(bad_1); // Type error!, bad_record cannot be put with another branded type here
const bad_2 = z.object({
branded_2,
bad_record,
});
testFn(bad_2); // Type error! it doesn't matter if it's mixed with the branded type it uses, any other branded type will cause the issue
const bad_3 = z.object({
a: z.object({
branded_2,
}),
b: z.object({
bad_record,
}),
});
testFn(bad_3); // Type error! Issue still occurs when separately nested
Errors are:
mre.ts(45,8): error TS2345: Argument of type 'ZodObject<{ branded_1: ZodBranded<ZodString, "type1">; bad_record: ZodRecord<ZodBranded<ZodString, "type1">, ZodString>; }, "strip", ZodTypeAny, { ...; }, { ...; }>' is not assignable to parameter of type 'ZodType<{ branded_1: string & BRAND<"type1">; bad_record: Partial<Record<string & BRAND<"type1">, string>>; }, ZodTypeDef, { branded_1: string & BRAND<...>; bad_record: Partial<...>; }>'.
The types of '_input.branded_1' are incompatible between these types.
Type 'string' is not assignable to type 'string & BRAND<"type1">'.
Type 'string' is not assignable to type 'BRAND<"type1">'.
mre.ts(51,8): error TS2345: Argument of type 'ZodObject<{ branded_2: ZodBranded<ZodString, "type2">; bad_record: ZodRecord<ZodBranded<ZodString, "type1">, ZodString>; }, "strip", ZodTypeAny, { ...; }, { ...; }>' is not assignable to parameter of type 'ZodType<{ branded_2: string & BRAND<"type2">; bad_record: Partial<Record<string & BRAND<"type1">, string>>; }, ZodTypeDef, { branded_2: string & BRAND<...>; bad_record: Partial<...>; }>'.
The types of '_input.branded_2' are incompatible between these types.
Type 'string' is not assignable to type 'string & BRAND<"type2">'.
Type 'string' is not assignable to type 'BRAND<"type2">'.
mre.ts(61,8): error TS2345: Argument of type 'ZodObject<{ a: ZodObject<{ branded_2: ZodBranded<ZodString, "type2">; }, "strip", ZodTypeAny, { branded_2: string & BRAND<"type2">; }, { branded_2: string; }>; b: ZodObject<...>; }, "strip", ZodTypeAny, { ...; }, { ...; }>' is not assignable to parameter of type 'ZodType<{ a: { branded_2: string & BRAND<"type2">; }; b: { bad_record: Partial<Record<string & BRAND<"type1">, string>>; }; }, ZodTypeDef, { ...; }>'.
The types of '_input.a.branded_2' are incompatible between these types.
Type 'string' is not assignable to type 'string & BRAND<"type2">'.
Expected
testFn should take any instance of ZodType, which ZodObject extends, without error.
Versions
Zod: 3.23.8 Typescript: 5.6.3
Observations
Errors are:
Expected
testFn
should take any instance ofZodType
, whichZodObject
extends, without error.