@@ -149,7 +149,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
149149
150150  // Register builtin types 
151151  for  ( const  name  of  [ 
152-     'string' ,  'boolean' ,  'number' ,  'null' 
152+     'string' ,  'boolean' ,  'number' ,  'null' ,   'void' ,   'binary' 
153153  ] )  { 
154154    const  typeName  =  { 
155155      namespace : '_builtins' , 
@@ -541,33 +541,29 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
541541  } 
542542
543543  function  validateTypeAlias  ( typeDef : model . TypeAlias ) : void   { 
544-     const  openGenerics  =  new   Set ( typeDef . generics ?. map ( t   =>   t . name ) ) 
544+     const  openGenerics  =  openGenericSet ( typeDef ) 
545545
546546    if  ( typeDef . variants  !=  null )  { 
547-       if  ( typeDef . generics  !=  null  &&  typeDef . generics . length  !==  0 )  { 
548-         modelError ( 'A tagged union should not have generic parameters' ) 
549-       } 
550- 
551547      if  ( typeDef . type . kind  !==  'union_of' )  { 
552548        modelError ( 'The "variants" tag only applies to unions' ) 
553549      }  else  { 
554-         validateTaggedUnion ( typeDef . name ,  typeDef . type ,  typeDef . variants ) 
550+         validateTaggedUnion ( typeDef . name ,  typeDef . type ,  typeDef . variants ,   openGenerics ) 
555551      } 
556552    }  else  { 
557553      validateValueOf ( typeDef . type ,  openGenerics ) 
558554    } 
559555  } 
560556
561-   function  validateTaggedUnion  ( parentName : TypeName ,  valueOf : model . UnionOf ,  variants : model . InternalTag  |  model . ExternalTag  |  model . Untagged ) : void   { 
557+   function  validateTaggedUnion  ( parentName : TypeName ,  valueOf : model . UnionOf ,  variants : model . InternalTag  |  model . ExternalTag  |  model . Untagged ,   openGenerics :  Set < string > ) : void   { 
562558    if  ( variants . kind  ===  'external_tag' )  { 
563559      // All items must have a 'variant' attribute 
564-       const  items  =  flattenUnionMembers ( valueOf ) 
560+       const  items  =  flattenUnionMembers ( valueOf ,   openGenerics ) 
565561
566562      for  ( const  item  of  items )  { 
567563        if  ( item . kind  !==  'instance_of' )  { 
568564          modelError ( 'Items of externally tagged unions must be types with a "variant_tag" annotation' ) 
569565        }  else  { 
570-           validateTypeRef ( item . type ,  undefined ,   new   Set < string > ( ) ) 
566+           validateTypeRef ( item . type ,  item . generics ,   openGenerics ) 
571567          const  type  =  getTypeDef ( item . type ) 
572568          if  ( type  ==  null )  { 
573569            modelError ( `Type ${ fqn ( item . type ) }   not found` ) 
@@ -582,13 +578,13 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
582578      } 
583579    }  else  if  ( variants . kind  ===  'internal_tag' )  { 
584580      const  tagName  =  variants . tag 
585-       const  items  =  flattenUnionMembers ( valueOf ) 
581+       const  items  =  flattenUnionMembers ( valueOf ,   openGenerics ) 
586582
587583      for  ( const  item  of  items )  { 
588584        if  ( item . kind  !==  'instance_of' )  { 
589585          modelError ( 'Items of internally tagged unions must be type references' ) 
590586        }  else  { 
591-           validateTypeRef ( item . type ,  undefined ,   new   Set < string > ( ) ) 
587+           validateTypeRef ( item . type ,  item . generics ,   openGenerics ) 
592588          const  type  =  getTypeDef ( item . type ) 
593589          if  ( type  ==  null )  { 
594590            modelError ( `Type ${ fqn ( item . type ) }   not found` ) 
@@ -601,7 +597,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
601597        } 
602598      } 
603599
604-       validateValueOf ( valueOf ,  new   Set ( ) ) 
600+       validateValueOf ( valueOf ,  openGenerics ) 
605601    }  else  if  ( variants . kind  ===  'untagged' )  { 
606602      if  ( fqn ( parentName )  !==  '_types.query_dsl:DecayFunction'  && 
607603          fqn ( parentName )  !==  '_types.query_dsl:DistanceFeatureQuery'  && 
@@ -614,15 +610,15 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
614610        modelError ( `Type ${ fqn ( variants . untypedVariant ) }   not found` ) 
615611      } 
616612
617-       const  items  =  flattenUnionMembers ( valueOf ) 
613+       const  items  =  flattenUnionMembers ( valueOf ,   openGenerics ) 
618614      const  baseTypes  =  new  Set < string > ( ) 
619615      let  foundUntyped  =  false 
620616
621617      for  ( const  item  of  items )  { 
622618        if  ( item . kind  !==  'instance_of' )  { 
623619          modelError ( 'Items of type untagged unions must be type references' ) 
624620        }  else  { 
625-           validateTypeRef ( item . type ,  undefined ,   new   Set < string > ( ) ) 
621+           validateTypeRef ( item . type ,  item . generics ,   openGenerics ) 
626622          const  type  =  getTypeDef ( item . type ) 
627623          if  ( type  ==  null )  { 
628624            modelError ( `Type ${ fqn ( item . type ) }   not found` ) 
@@ -674,7 +670,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
674670  // ----------------------------------------------------------------------------------------------- 
675671  // Constituents of type definitions 
676672
677-   function  openGenericSet  ( typeDef : model . Request  |  model . Response  |  model . Interface ) : Set < string >  { 
673+   function  openGenericSet  ( typeDef : model . Request  |  model . Response  |  model . Interface   |   model . TypeAlias ) : Set < string >  { 
678674    return  new  Set ( ( typeDef . generics  ??  [ ] ) . map ( name  =>  fqn ( name ) ) ) 
679675  } 
680676
@@ -879,7 +875,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
879875        }  else  { 
880876          // tagged union: the discriminant tells us what to look for, check each member in isolation 
881877          assert ( typeDef . type . kind  ===  'union_of' ,  'Variants are only allowed on union_of type aliases' ) 
882-           for  ( const  item  of  flattenUnionMembers ( typeDef . type ) )  { 
878+           for  ( const  item  of  flattenUnionMembers ( typeDef . type ,   new   Set ( ) ) )  { 
883879            validateValueOfJsonEvents ( item ) 
884880          } 
885881
@@ -894,13 +890,13 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
894890  } 
895891
896892  /** Build the flattened item list of potentially nested unions (this is used for large unions) */ 
897-   function  flattenUnionMembers  ( union : model . UnionOf ) : model . ValueOf [ ]  { 
893+   function  flattenUnionMembers  ( union : model . UnionOf ,   openGenerics :  Set < string > ) : model . ValueOf [ ]  { 
898894    const  allItems  =  new  Array < model . ValueOf > ( ) 
899895
900896    function  collectItems  ( items : model . ValueOf [ ] ) : void   { 
901897      for  ( const  item  of  items )  { 
902898        if  ( item . kind  !==  'instance_of' )  { 
903-           validateValueOf ( item ,  new   Set < string > ( ) ) 
899+           validateValueOf ( item ,  openGenerics ) 
904900          allItems . push ( item ) 
905901        }  else  { 
906902          const  itemType  =  getTypeDef ( item . type ) 
@@ -910,7 +906,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
910906            // Recurse in nested union 
911907            collectItems ( itemType . type . items ) 
912908          }  else  { 
913-             validateValueOf ( item ,  new   Set < string > ( ) ) 
909+             validateValueOf ( item ,  openGenerics ) 
914910            allItems . push ( item ) 
915911          } 
916912        } 
0 commit comments