@@ -279,6 +279,94 @@ describe("argument inference", () => {
279279 } ) ;
280280} ) ;
281281
282+ describe ( "argument inference 2: args and validators" , ( ) => {
283+ // Test with mutation, but all the wrappers work the same way.
284+ const mutation : MutationBuilder < any , "public" > = ( ( ) => {
285+ // Intentional noop. We're only testing the type
286+ } ) as any ;
287+
288+ const module = {
289+ argsSubtypeOfArgsValidator : mutation ( {
290+ args : { foo : v . string ( ) } ,
291+ handler : ( _ , { foo } : { foo : string | number } ) => 1 ,
292+ } ) ,
293+ argsSupertypeOfArgsValidator : mutation ( {
294+ args : { foo : v . union ( v . string ( ) , v . number ( ) ) } ,
295+ handler : ( _ , { foo } : { foo : string } ) => 1 ,
296+ } ) ,
297+ argsPartiallyIntersectArgsValidator : mutation ( {
298+ args : { foo : v . union ( v . string ( ) , v . number ( ) ) } ,
299+ // @ts -expect-error when neither type is a subtype of the other, error
300+ handler : ( _ , { foo } : { foo : string | Array < number > } ) => 1 ,
301+ } ) ,
302+ argsDontIntersectArgsValidator : mutation ( {
303+ args : { foo : v . string ( ) } ,
304+ // @ts -expect-error when neither type is a subtype of the other, error
305+ handler : ( _ , { foo } : { foo : number } ) => 1 ,
306+ } ) ,
307+ } ;
308+
309+ type API = ApiFromModules < { module : typeof module } > ;
310+
311+ // If the validator is more specific, that's the type that will be used.
312+ // That seems right.
313+ test ( "Handler args are a subtype of args validator" , ( ) => {
314+ type Args = API [ "module" ] [ "argsSubtypeOfArgsValidator" ] [ "_args" ] ;
315+ assert < Equals < Args , { foo : string } > > ( ) ;
316+ } ) ;
317+ // If the argument type is more specific, that's the type used.
318+ // It would be nice to change this. If the validator type
319+ // could entirely determine the inferred type on the client
320+ // then you could skip the function for inference!
321+ test ( "Handler args are a supertype of args validator" , ( ) => {
322+ type Args = API [ "module" ] [ "argsSupertypeOfArgsValidator" ] [ "_args" ] ;
323+ assert < Equals < Args , { foo : string } > > ( ) ;
324+ } ) ;
325+ } ) ;
326+
327+ describe ( "argument inference 3: outputs and validators" , ( ) => {
328+ // Test with mutation, but all the wrappers work the same way.
329+ const mutation : MutationBuilder < any , "public" > = ( ( ) => {
330+ // Intentional noop. We're only testing the type
331+ } ) as any ;
332+
333+ const module = {
334+ returnSubtypeOfReturnsValidator : mutation ( {
335+ returns : v . string ( ) ,
336+ handler : ( _ ) => "a" as const ,
337+ } ) ,
338+ returnSupertypeOfReturnsValidator : mutation ( {
339+ returns : v . literal ( "a" ) ,
340+ // @ts -expect-error when return value is not a subtype of validator, error
341+ handler : ( _ ) => "b" as "b" | "a" ,
342+ } ) ,
343+ returnPartiallyIntersectReturnsValidator : mutation ( {
344+ returns : v . union ( v . literal ( "a" ) , v . literal ( "b" ) ) ,
345+ // @ts -expect-error when return value is not a subtype of validator, error
346+ handler : ( _ ) => "b" as "b" | "c" ,
347+ } ) ,
348+ returnDoesntIntersectReturnsValidator : mutation ( {
349+ returns : v . literal ( "a" ) ,
350+ // @ts -expect-error when neither type is a subtype of the other, error
351+ handler : ( _ ) => "b" as const ,
352+ } ) ,
353+ } ;
354+
355+ type API = ApiFromModules < { module : typeof module } > ;
356+
357+ // Output validators seem to work about right.
358+ test ( "Handler return value is a subtype of returns validator" , ( ) => {
359+ type ReturnType =
360+ API [ "module" ] [ "returnSubtypeOfReturnsValidator" ] [ "_returnType" ] ;
361+ assert < Equals < ReturnType , "a" > > ( ) ;
362+ } ) ;
363+ test ( "Handler return value is a supertype of returns validator" , ( ) => {
364+ type ReturnType =
365+ API [ "module" ] [ "returnSupertypeOfReturnsValidator" ] [ "_returnType" ] ;
366+ assert < Equals < ReturnType , any > > ( ) ;
367+ } ) ;
368+ } ) ;
369+
282370describe ( "argument and return value validators can be objects or validators" , ( ) => {
283371 // Test with mutation, we aim for all the wrappers work the same way.
284372 const mutation : MutationBuilder < any , "public" > = mutationGeneric ;
0 commit comments