1- import { expect , test } from "vitest" ;
1+ import { expect , expectTypeOf , test } from "vitest" ;
22
33import * as z from "zod/v4" ;
44
@@ -15,6 +15,37 @@ test("string to number pipe async", async () => {
1515 expect ( await schema . parseAsync ( "1234" ) ) . toEqual ( 1234 ) ;
1616} ) ;
1717
18+ test ( "pipe contextually types transforms" , ( ) => {
19+ const schema = z . string ( ) . pipe ( z . transform ( ( val ) => val . toUpperCase ( ) ) ) ;
20+ expectTypeOf < z . output < typeof schema > > ( ) . toEqualTypeOf < string > ( ) ;
21+
22+ // @ts -expect-error incompatible pipe targets are still rejected
23+ z . string ( ) . pipe ( z . number ( ) ) ;
24+ } ) ;
25+
26+ test ( "pipe branded output to unbranded input" , ( ) => {
27+ const zodBrand = z . string ( ) . brand < "myBrand" > ( ) ;
28+ const inputSchema = z . object ( {
29+ a : z . number ( ) ,
30+ c : zodBrand ,
31+ } ) ;
32+ const validateSchema = z . object ( {
33+ a : z . number ( ) ,
34+ c : zodBrand ,
35+ } ) ;
36+
37+ const testSchemaPipeline = inputSchema . transform ( ( input ) => input ) . pipe ( validateSchema ) ;
38+ const testSchemaPipeline2 = inputSchema . pipe ( validateSchema ) ;
39+ const testSchemaPipeline3 = inputSchema . pipe ( inputSchema ) ;
40+
41+ expectTypeOf < z . output < typeof testSchemaPipeline > > ( ) . toEqualTypeOf < {
42+ a : number ;
43+ c : string & z . core . $brand < "myBrand" > ;
44+ } > ( ) ;
45+ expectTypeOf < z . output < typeof testSchemaPipeline2 > > ( ) . toEqualTypeOf < z . output < typeof validateSchema > > ( ) ;
46+ expectTypeOf < z . output < typeof testSchemaPipeline3 > > ( ) . toEqualTypeOf < z . output < typeof inputSchema > > ( ) ;
47+ } ) ;
48+
1849test ( "string with default fallback" , ( ) => {
1950 const stringWithDefault = z
2051 . pipe (
0 commit comments