diff --git a/deno/lib/__tests__/branded.test.ts b/deno/lib/__tests__/branded.test.ts index ff663ef66..5e775ab29 100644 --- a/deno/lib/__tests__/branded.test.ts +++ b/deno/lib/__tests__/branded.test.ts @@ -47,8 +47,8 @@ test("branded types", () => { true ); - // keeping brands out of input types - const age = z.number().brand<"age">(); + // keeping brands out of input types in non-strict mode + const age = z.number().brand<"age", false>(); type Age = z.infer; type AgeInput = z.input; @@ -59,4 +59,10 @@ test("branded types", () => { // @ts-expect-error doStuff({ name: "hello there!" }); + + // (default) strict mode - should be branded + const height = z.number().brand("metricHeight"); + + type Height = z.input; + util.assertEqual, Height>(true); }); diff --git a/deno/lib/helpers/parseUtil.ts b/deno/lib/helpers/parseUtil.ts index 8df221464..0e4da903c 100644 --- a/deno/lib/helpers/parseUtil.ts +++ b/deno/lib/helpers/parseUtil.ts @@ -112,9 +112,11 @@ export class ParseStatus { ): Promise> { const syncPairs: ObjectPair[] = []; for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; syncPairs.push({ - key: await pair.key, - value: await pair.value, + key, + value, }); } return ParseStatus.mergeObjectSync(status, syncPairs); diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 279f4e44b..911451695 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -466,8 +466,16 @@ export abstract class ZodType< }) as any; } - brand(brand?: B): ZodBranded; - brand(): ZodBranded { + brand( + brand?: B, + opts?: { + strict: S; + } + ): ZodBranded; + brand< + B extends string | number | symbol, + S extends boolean = true + >(): ZodBranded { return new ZodBranded({ typeName: ZodFirstPartyTypeKind.ZodBranded, type: this, @@ -2357,9 +2365,10 @@ export class ZodObject< const syncPairs: any[] = []; for (const pair of pairs) { const key = await pair.key; + const value = await pair.value; syncPairs.push({ key, - value: await pair.value, + value, alwaysSet: pair.alwaysSet, }); } @@ -4726,8 +4735,13 @@ export type BRAND = { export class ZodBranded< T extends ZodTypeAny, - B extends string | number | symbol -> extends ZodType, ZodBrandedDef, T["_input"]> { + B extends string | number | symbol, + S extends boolean +> extends ZodType< + T["_output"] & BRAND, + ZodBrandedDef, + S extends true ? T["_input"] & BRAND : T["_input"] +> { _parse(input: ParseInput): ParseReturnType { const { ctx } = this._processInputParams(input); const data = ctx.data; @@ -4999,7 +5013,7 @@ export type ZodFirstPartySchemaTypes = | ZodDefault | ZodCatch | ZodPromise - | ZodBranded + | ZodBranded | ZodPipeline | ZodReadonly | ZodSymbol; diff --git a/src/__tests__/branded.test.ts b/src/__tests__/branded.test.ts index d30dfeb83..f3c38ee7a 100644 --- a/src/__tests__/branded.test.ts +++ b/src/__tests__/branded.test.ts @@ -46,8 +46,8 @@ test("branded types", () => { true ); - // keeping brands out of input types - const age = z.number().brand<"age">(); + // keeping brands out of input types in non-strict mode + const age = z.number().brand<"age", false>(); type Age = z.infer; type AgeInput = z.input; @@ -58,4 +58,10 @@ test("branded types", () => { // @ts-expect-error doStuff({ name: "hello there!" }); + + // (default) strict mode - should be branded + const height = z.number().brand("metricHeight"); + + type Height = z.input; + util.assertEqual, Height>(true); }); diff --git a/src/types.ts b/src/types.ts index 6a2d8c1f8..effe80b6a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -466,8 +466,16 @@ export abstract class ZodType< }) as any; } - brand(brand?: B): ZodBranded; - brand(): ZodBranded { + brand( + brand?: B, + opts?: { + strict: S; + } + ): ZodBranded; + brand< + B extends string | number | symbol, + S extends boolean = true + >(): ZodBranded { return new ZodBranded({ typeName: ZodFirstPartyTypeKind.ZodBranded, type: this, @@ -4727,8 +4735,13 @@ export type BRAND = { export class ZodBranded< T extends ZodTypeAny, - B extends string | number | symbol -> extends ZodType, ZodBrandedDef, T["_input"]> { + B extends string | number | symbol, + S extends boolean +> extends ZodType< + T["_output"] & BRAND, + ZodBrandedDef, + S extends true ? T["_input"] & BRAND : T["_input"] +> { _parse(input: ParseInput): ParseReturnType { const { ctx } = this._processInputParams(input); const data = ctx.data; @@ -5000,7 +5013,7 @@ export type ZodFirstPartySchemaTypes = | ZodDefault | ZodCatch | ZodPromise - | ZodBranded + | ZodBranded | ZodPipeline | ZodReadonly | ZodSymbol;