From a9c0aeaa59021f3de35a90393fa8c98362ba1768 Mon Sep 17 00:00:00 2001 From: Carlos Cobo Date: Tue, 29 Jul 2025 13:29:26 +0200 Subject: [PATCH 1/2] feat: avoid generating repeated type constraints --- bindings/declarations.go | 43 ++++++++++++++++++++++++++-------------- testdata/union/union.go | 1 - testdata/union/union.ts | 2 +- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/bindings/declarations.go b/bindings/declarations.go index 19dacc1..7b866e9 100644 --- a/bindings/declarations.go +++ b/bindings/declarations.go @@ -1,10 +1,5 @@ package bindings -import ( - "fmt" - "reflect" -) - // DeclarationType is any type that can exist at the top level of a AST. // Meaning it can be serialized into valid Typescript. type DeclarationType interface { @@ -69,23 +64,41 @@ func (p *TypeParameter) isNode() {} // Simplify removes duplicate type parameters func Simplify(p []*TypeParameter) ([]*TypeParameter, error) { - params := make([]*TypeParameter, 0, len(p)) - exists := make(map[string]*TypeParameter) + params := []*TypeParameter{} + set := make(map[string]bool) for _, tp := range p { - if found, ok := exists[tp.Name.Ref()]; ok { - // Compare types, make sure they are the same - equal := reflect.DeepEqual(found, tp) - if !equal { - return nil, fmt.Errorf("type parameter %q already exists with different type", tp.Name) + ref := tp.Name.Ref() + if _, ok := set[ref]; !ok { + params = append(params, tp) + set[ref] = true + + if union, ok := tp.Type.(*UnionType); ok { + simplifyUnion(union) } - continue } - params = append(params, tp) - exists[tp.Name.Ref()] = tp } return params, nil } +func simplifyUnion(union *UnionType) *UnionType { + types := []ExpressionType{} + set := map[string]bool{} + for _, arg := range union.Types { + switch v := arg.(type) { + case *LiteralKeyword: + key := v.String() + if _, ok := set[key]; !ok { + set[key] = true + types = append(types, arg) + } + default: + types = append(types, arg) + } + } + union.Types = types + return union +} + // VariableStatement is a top level declaration of a variable // var foo: string = "bar" // const foo: string = "bar" diff --git a/testdata/union/union.go b/testdata/union/union.go index bd228f3..9171f14 100644 --- a/testdata/union/union.go +++ b/testdata/union/union.go @@ -5,7 +5,6 @@ type UnionConstraint[T string | int64] struct { } // Repeated constraints are redundant -// TODO: Write a mutation to remove redundant constraints type Repeated[T string | string | int64 | uint64] struct { Value T } diff --git a/testdata/union/union.ts b/testdata/union/union.ts index e0282cc..15f2eb1 100644 --- a/testdata/union/union.ts +++ b/testdata/union/union.ts @@ -1,7 +1,7 @@ // Code generated by 'guts'. DO NOT EDIT. // From union/union.go -export interface Repeated { +export interface Repeated { readonly Value: T; } From 8992995219acbdfc977bc6d9b1ce5053b7e074a3 Mon Sep 17 00:00:00 2001 From: Carlos Cobo Date: Tue, 29 Jul 2025 17:01:32 +0200 Subject: [PATCH 2/2] emphasize this only works with literals --- bindings/declarations.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bindings/declarations.go b/bindings/declarations.go index 7b866e9..4fb74eb 100644 --- a/bindings/declarations.go +++ b/bindings/declarations.go @@ -73,22 +73,22 @@ func Simplify(p []*TypeParameter) ([]*TypeParameter, error) { set[ref] = true if union, ok := tp.Type.(*UnionType); ok { - simplifyUnion(union) + simplifyUnionLiterals(union) } } } return params, nil } -func simplifyUnion(union *UnionType) *UnionType { +func simplifyUnionLiterals(union *UnionType) *UnionType { types := []ExpressionType{} - set := map[string]bool{} + literalSet := map[string]bool{} for _, arg := range union.Types { switch v := arg.(type) { case *LiteralKeyword: key := v.String() - if _, ok := set[key]; !ok { - set[key] = true + if _, ok := literalSet[key]; !ok { + literalSet[key] = true types = append(types, arg) } default: