Skip to content

Commit 1398093

Browse files
committed
chore: simplify null union elements
not a perfect solution, might require an ast walker that can replace nodes to get it working perfectly
1 parent 7eee3b8 commit 1398093

File tree

6 files changed

+50
-41
lines changed

6 files changed

+50
-41
lines changed

.github/workflows/release.yml

-40
This file was deleted.

bindings/bindings.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ func (b *Bindings) Union(ty *UnionType) (*goja.Object, error) {
395395
return nil, err
396396
}
397397

398-
var types []interface{}
398+
var types []any
399399
for _, t := range ty.Types {
400400
v, err := b.ToTypescriptExpressionNode(t)
401401
if err != nil {

config/mutations.go

+46
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"log/slog"
66
"reflect"
7+
"slices"
78
"strings"
89

910
"github.com/coder/guts"
@@ -223,3 +224,48 @@ func (h *hasAnyVisitor) Visit(node bindings.Node) walk.Visitor {
223224
}
224225
return h
225226
}
227+
228+
// NullUnionSlices converts slices with nullable elements to remove the 'null'
229+
// type from the union.
230+
// This happens when a golang pointer is the element type of a slice.
231+
// Example:
232+
// GolangType: []*string
233+
// TsType: (string | null)[] --> (string)[]
234+
// TODO: Somehow remove the parenthesis from the output type.
235+
// Might have to change the node from a union type to it's first element.
236+
func NullUnionSlices(ts *guts.Typescript) {
237+
ts.ForEach(func(key string, node bindings.Node) {
238+
walk.Walk(&nullUnionVisitor{}, node)
239+
})
240+
}
241+
242+
type nullUnionVisitor struct{}
243+
244+
func (v *nullUnionVisitor) Visit(node bindings.Node) walk.Visitor {
245+
if array, ok := node.(*bindings.ArrayType); ok {
246+
// Is array
247+
if union, ok := array.Node.(*bindings.UnionType); ok {
248+
hasNull := slices.ContainsFunc(union.Types, func(t bindings.ExpressionType) bool {
249+
_, isNull := t.(*bindings.Null)
250+
return isNull
251+
})
252+
253+
// With union type
254+
if len(union.Types) == 2 && hasNull {
255+
// A union of 2 types, one being null
256+
// Remove the null type
257+
newTypes := make([]bindings.ExpressionType, 0, 1)
258+
for _, t := range union.Types {
259+
if _, isNull := t.(*bindings.Null); isNull {
260+
continue
261+
}
262+
newTypes = append(newTypes, t)
263+
}
264+
union.Types = newTypes
265+
266+
}
267+
}
268+
}
269+
270+
return v
271+
}

convert_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ func TestGeneration(t *testing.T) {
9696
config.EnumLists,
9797
config.ExportTypes,
9898
config.ReadOnly,
99+
config.NullUnionSlices,
99100
)
100101

101102
output, err := ts.Serialize()

testdata/nullable/nullable.go

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type NullableFields struct {
77
Nullable *string `json:"nullable"`
88
NullableOmitEmpty *string `json:"nullableOmitEmpty,omitempty"`
99
NullTime sql.NullTime `json:"nullTime"`
10+
SlicePointer []*string `json:"slicePointer"`
1011
}
1112

1213
type EmptyFields struct {

testdata/nullable/nullable.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export interface NullableFields {
1212
readonly nullable: string | null;
1313
readonly nullableOmitEmpty?: string | null;
1414
readonly nullTime: string | null;
15+
readonly slicePointer: readonly (string)[];
1516
}

0 commit comments

Comments
 (0)