@@ -462,70 +462,8 @@ func (g *generator) genInterface(name string, v cue.Value) []ts.Decl {
462
462
return nil
463
463
}
464
464
465
- // Create an empty value, onto which we'll unify fields that need not be
466
- // generated as literals.
467
- nolit := v .Context ().CompileString ("{...}" )
468
-
469
- var extends []ts.Expr
470
- var some bool
471
-
472
- // Recursively walk down Values returned from Expr() and separate
473
- // unified/embedded structs from a struct literal, so that we can make the
474
- // former (if they are also marked with @cuetsy(kind="interface")) show up
475
- // as "extends" instead of writing out their fields directly.
476
- var walkExpr func (wv cue.Value ) error
477
- walkExpr = func (wv cue.Value ) error {
478
- op , dvals := wv .Expr ()
479
- switch op {
480
- case cue .NoOp :
481
- // Simple path - when the field is a plain struct literal decl, the walk function
482
- // will take this branch and return immediately.
483
-
484
- // FIXME this does the struct literal path correctly, but it also
485
- // catches this case, for some reason:
486
- //
487
- // Thing: {
488
- // other.Thing
489
- // }
490
- //
491
- // The saner form - `Thing: other.Thing` - does not go through this path.
492
- return nil
493
- case cue .OrOp :
494
- return valError (wv , "typescript interfaces cannot be constructed from disjunctions" )
495
- case cue .SelectorOp :
496
- expr , err := refAsInterface (wv )
497
- if err != nil {
498
- return err
499
- }
500
-
501
- // If we have a string to add to the list of "extends", then also
502
- // add the ref to the list of fields to exclude if subsumed.
503
- if expr != nil {
504
- some = true
505
- extends = append (extends , expr )
506
- nolit = nolit .Unify (cue .Dereference (wv ))
507
- }
508
- return nil
509
- case cue .AndOp :
510
- // First, search the dvals for StructLits. Having more than one is possible,
511
- // but weird, as writing >1 literal and unifying them is the same as just writing
512
- // one containing the unified result - more complicated with no obvious benefit.
513
- for _ , dv := range dvals {
514
- if dv .IncompleteKind () != cue .StructKind && dv .IncompleteKind () != cue .TopKind {
515
- panic ("impossible? seems like it should be. if this pops, clearly not!" )
516
- }
517
-
518
- if err := walkExpr (dv ); err != nil {
519
- return err
520
- }
521
- }
522
- return nil
523
- default :
524
- panic (fmt .Sprintf ("unhandled op type %s" , op .String ()))
525
- }
526
- }
527
-
528
- if err := walkExpr (v ); err != nil {
465
+ extends , nolit , err := findExtends (v )
466
+ if err != nil {
529
467
g .addErr (err )
530
468
return nil
531
469
}
@@ -559,15 +497,15 @@ func (g *generator) genInterface(name string, v cue.Value) []ts.Decl {
559
497
//
560
498
// There's _probably_ a way around this, especially when we move to an
561
499
// AST rather than dumb string templates. But i'm tired of looking.
562
- if some {
500
+ if len ( extends ) > 0 {
563
501
// Look up the path of the current field within the nolit value,
564
502
// then check it for subsumption.
565
503
sel := iter .Selector ()
566
504
if iter .IsOptional () {
567
505
sel = sel .Optional ()
568
506
}
569
- sub := nolit .LookupPath (cue .MakePath (sel ))
570
507
508
+ sub := nolit .LookupPath (cue .MakePath (sel ))
571
509
// Theoretically, lattice equality can be defined as bijective
572
510
// subsumption. In practice, Subsume() seems to ignore optional
573
511
// fields, and Equals() doesn't. So, use Equals().
@@ -636,6 +574,74 @@ func (g *generator) genInterface(name string, v cue.Value) []ts.Decl {
636
574
return ret
637
575
}
638
576
577
+ // Recursively walk down Values returned from Expr() and separate
578
+ // unified/embedded structs from a struct literal, so that we can make the
579
+ // former (if they are also marked with @cuetsy(kind="interface")) show up
580
+ // as "extends" instead of inlining their fields.
581
+ func findExtends (v cue.Value ) ([]ts.Expr , cue.Value , error ) {
582
+ var extends []ts.Expr
583
+ // Create an empty value, onto which we'll unify fields that need not be
584
+ // generated as literals.
585
+ baseNolit := v .Context ().CompileString ("" )
586
+ nolit := v .Context ().CompileString ("" )
587
+ var walkExpr func (v cue.Value ) error
588
+ walkExpr = func (v cue.Value ) error {
589
+ op , dvals := v .Expr ()
590
+ switch op {
591
+ case cue .NoOp :
592
+ // Simple path - when the field is a plain struct literal decl, the walk function
593
+ // will take this branch and return immediately.
594
+
595
+ // FIXME this does the struct literal path correctly, but it also
596
+ // catches this case, for some reason:
597
+ //
598
+ // Thing: {
599
+ // other.Thing
600
+ // }
601
+ //
602
+ // The saner form - `Thing: other.Thing` - does not go through this path.
603
+ return nil
604
+ case cue .OrOp :
605
+ return valError (v , "typescript interfaces cannot be constructed from disjunctions" )
606
+ case cue .SelectorOp :
607
+ expr , err := refAsInterface (v )
608
+ if err != nil {
609
+ return err
610
+ }
611
+
612
+ // If we have a string to add to the list of "extends", then also
613
+ // add the ref to the list of fields to exclude if subsumed.
614
+ if expr != nil {
615
+ extends = append (extends , expr )
616
+ nolit = baseNolit .Unify (nolit .Unify (cue .Dereference (v )))
617
+ }
618
+ return nil
619
+ case cue .AndOp :
620
+ // First, search the dvals for StructLits. Having more than one is possible,
621
+ // but weird, as writing >1 literal and unifying them is the same as just writing
622
+ // one containing the unified result - more complicated with no obvious benefit.
623
+ for _ , dv := range dvals {
624
+ if dv .IncompleteKind () != cue .StructKind && dv .IncompleteKind () != cue .TopKind {
625
+ panic ("impossible? seems like it should be. if this pops, clearly not!" )
626
+ }
627
+
628
+ if err := walkExpr (dv ); err != nil {
629
+ return err
630
+ }
631
+ }
632
+ return nil
633
+ default :
634
+ panic (fmt .Sprintf ("unhandled op type %s" , op .String ()))
635
+ }
636
+ }
637
+
638
+ if err := walkExpr (v ); err != nil {
639
+ return nil , nolit , err
640
+ }
641
+
642
+ return extends , nolit , nil
643
+ }
644
+
639
645
// Generate a typeRef for the cue.Value
640
646
func (g * generator ) genInterfaceField (v cue.Value ) (* typeRef , error ) {
641
647
tref := & typeRef {}
0 commit comments