diff --git a/v2/internal/binding/binding.go b/v2/internal/binding/binding.go index b7794876bf2..d2b437f2b12 100644 --- a/v2/internal/binding/binding.go +++ b/v2/internal/binding/binding.go @@ -262,22 +262,19 @@ func (b *Bindings) AddStructToGenerateTS(packageName string, structName string, // Iterate this struct and add any struct field references structType := reflect.TypeOf(s) - if hasElements(structType) { + for hasElements(structType) { structType = structType.Elem() } for i := 0; i < structType.NumField(); i++ { field := structType.Field(i) - if field.Anonymous { + if field.Anonymous || !field.IsExported() { continue } kind := field.Type.Kind() if kind == reflect.Struct { - if !field.IsExported() { - continue - } fqname := field.Type.String() - sNameSplit := strings.Split(fqname, ".") + sNameSplit := strings.SplitN(fqname, ".", 2) if len(sNameSplit) < 2 { continue } @@ -288,22 +285,24 @@ func (b *Bindings) AddStructToGenerateTS(packageName string, structName string, s := reflect.Indirect(a).Interface() b.AddStructToGenerateTS(pName, sName, s) } - } else if hasElements(field.Type) && field.Type.Elem().Kind() == reflect.Struct { - if !field.IsExported() { - continue - } - fqname := field.Type.Elem().String() - sNameSplit := strings.Split(fqname, ".") - if len(sNameSplit) < 2 { - continue + } else { + fType := field.Type + for hasElements(fType) { + fType = fType.Elem() } - sName := sNameSplit[1] - pName := getPackageName(fqname) - typ := field.Type.Elem() - a := reflect.New(typ) - if b.hasExportedJSONFields(typ) { - s := reflect.Indirect(a).Interface() - b.AddStructToGenerateTS(pName, sName, s) + if fType.Kind() == reflect.Struct { + fqname := fType.String() + sNameSplit := strings.SplitN(fqname, ".", 2) + if len(sNameSplit) < 2 { + continue + } + sName := sNameSplit[1] + pName := getPackageName(fqname) + a := reflect.New(fType) + if b.hasExportedJSONFields(fType) { + s := reflect.Indirect(a).Interface() + b.AddStructToGenerateTS(pName, sName, s) + } } } } diff --git a/v2/internal/binding/binding_test/binding_deepelements_test.go b/v2/internal/binding/binding_test/binding_deepelements_test.go new file mode 100644 index 00000000000..488c58f6d13 --- /dev/null +++ b/v2/internal/binding/binding_test/binding_deepelements_test.go @@ -0,0 +1,126 @@ +package binding_test + +// Issues 2303, 3442, 3709 + +type DeepMessage struct { + Msg string +} + +type DeepElements struct { + Single []int + Double [][]string + FourDouble [4][]float64 + DoubleFour [][4]int64 + Triple [][][]int + + SingleMap map[string]int + SliceMap map[string][]int + DoubleSliceMap map[string][][]int + + ArrayMap map[string][4]int + DoubleArrayMap1 map[string][4][]int + DoubleArrayMap2 map[string][][4]int + DoubleArrayMap3 map[string][4][4]int + + OneStructs []*DeepMessage + TwoStructs [3][]*DeepMessage + ThreeStructs [][][]DeepMessage + MapStructs map[string][]*DeepMessage + MapTwoStructs map[string][4][]DeepMessage + MapThreeStructs map[string][][7][]*DeepMessage +} + +func (x DeepElements) Get() DeepElements { + return x +} + +var DeepElementsTest = BindingTest{ + name: "DeepElements", + structs: []interface{}{ + &DeepElements{}, + }, + exemptions: nil, + shouldError: false, + want: ` +export namespace binding_test { + + export class DeepMessage { + Msg: string; + + static createFrom(source: any = {}) { + return new DeepMessage(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Msg = source["Msg"]; + } + } + export class DeepElements { + Single: number[]; + Double: string[][]; + FourDouble: number[][]; + DoubleFour: number[][]; + Triple: number[][][]; + SingleMap: Record; + SliceMap: Record; + DoubleSliceMap: Record; + ArrayMap: Record; + DoubleArrayMap1: Record; + DoubleArrayMap2: Record; + DoubleArrayMap3: Record; + OneStructs: DeepMessage[]; + TwoStructs: DeepMessage[][]; + ThreeStructs: DeepMessage[][][]; + MapStructs: Record; + MapTwoStructs: Record; + MapThreeStructs: Record; + + static createFrom(source: any = {}) { + return new DeepElements(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Single = source["Single"]; + this.Double = source["Double"]; + this.FourDouble = source["FourDouble"]; + this.DoubleFour = source["DoubleFour"]; + this.Triple = source["Triple"]; + this.SingleMap = source["SingleMap"]; + this.SliceMap = source["SliceMap"]; + this.DoubleSliceMap = source["DoubleSliceMap"]; + this.ArrayMap = source["ArrayMap"]; + this.DoubleArrayMap1 = source["DoubleArrayMap1"]; + this.DoubleArrayMap2 = source["DoubleArrayMap2"]; + this.DoubleArrayMap3 = source["DoubleArrayMap3"]; + this.OneStructs = this.convertValues(source["OneStructs"], DeepMessage); + this.TwoStructs = this.convertValues(source["TwoStructs"], DeepMessage); + this.ThreeStructs = this.convertValues(source["ThreeStructs"], DeepMessage); + this.MapStructs = this.convertValues(source["MapStructs"], DeepMessage[], true); + this.MapTwoStructs = this.convertValues(source["MapTwoStructs"], DeepMessage[][], true); + this.MapThreeStructs = this.convertValues(source["MapThreeStructs"], DeepMessage[][][], true); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + + } +`, +} diff --git a/v2/internal/binding/binding_test/binding_generics_test.go b/v2/internal/binding/binding_test/binding_generics_test.go new file mode 100644 index 00000000000..920bd2a7acf --- /dev/null +++ b/v2/internal/binding/binding_test/binding_generics_test.go @@ -0,0 +1,154 @@ +package binding_test + +import "github.com/wailsapp/wails/v2/internal/binding/binding_test/binding_test_import/float_package" + +// Issues 3900, 3371, 2323 (no TS generics though) + +type ListData[T interface{}] struct { + Total int64 `json:"Total"` + TotalPage int64 `json:"TotalPage"` + PageNum int `json:"PageNum"` + List []T `json:"List,omitempty"` +} + +func (x ListData[T]) Get() ListData[T] { + return x +} + +var Generics1Test = BindingTest{ + name: "Generics1", + structs: []interface{}{ + &ListData[string]{}, + }, + exemptions: nil, + shouldError: false, + want: ` +export namespace binding_test { + + export class ListData_string_ { + Total: number; + TotalPage: number; + PageNum: number; + List?: string[]; + + static createFrom(source: any = {}) { + return new ListData_string_(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Total = source["Total"]; + this.TotalPage = source["TotalPage"]; + this.PageNum = source["PageNum"]; + this.List = source["List"]; + } + } + + } +`, +} + +var Generics2Test = BindingTest{ + name: "Generics2", + structs: []interface{}{ + &ListData[float_package.SomeStruct]{}, + &ListData[*float_package.SomeStruct]{}, + }, + exemptions: nil, + shouldError: false, + want: ` +export namespace binding_test { + + export class ListData__github_com_wailsapp_wails_v2_internal_binding_binding_test_binding_test_import_float_package_SomeStruct_ { + Total: number; + TotalPage: number; + PageNum: number; + List?: float_package.SomeStruct[]; + + static createFrom(source: any = {}) { + return new ListData__github_com_wailsapp_wails_v2_internal_binding_binding_test_binding_test_import_float_package_SomeStruct_(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Total = source["Total"]; + this.TotalPage = source["TotalPage"]; + this.PageNum = source["PageNum"]; + this.List = this.convertValues(source["List"], float_package.SomeStruct); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class ListData_github_com_wailsapp_wails_v2_internal_binding_binding_test_binding_test_import_float_package_SomeStruct_ { + Total: number; + TotalPage: number; + PageNum: number; + List?: float_package.SomeStruct[]; + + static createFrom(source: any = {}) { + return new ListData_github_com_wailsapp_wails_v2_internal_binding_binding_test_binding_test_import_float_package_SomeStruct_(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Total = source["Total"]; + this.TotalPage = source["TotalPage"]; + this.PageNum = source["PageNum"]; + this.List = this.convertValues(source["List"], float_package.SomeStruct); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + + } + + export namespace float_package { + + export class SomeStruct { + string: string; + + static createFrom(source: any = {}) { + return new SomeStruct(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.string = source["string"]; + } + } + + } +`, +} diff --git a/v2/internal/binding/binding_test/binding_ignored_test.go b/v2/internal/binding/binding_test/binding_ignored_test.go new file mode 100644 index 00000000000..aeb6a9c3f0b --- /dev/null +++ b/v2/internal/binding/binding_test/binding_ignored_test.go @@ -0,0 +1,47 @@ +package binding_test + +import ( + "unsafe" +) + +// Issues 3755, 3809 + +type Ignored struct { + Valid bool + Total func() int `json:"Total"` + UnsafeP unsafe.Pointer + Complex64 complex64 `json:"Complex"` + Complex128 complex128 + StringChan chan string +} + +func (x Ignored) Get() Ignored { + return x +} + +var IgnoredTest = BindingTest{ + name: "Ignored", + structs: []interface{}{ + &Ignored{}, + }, + exemptions: nil, + shouldError: false, + want: ` +export namespace binding_test { + + export class Ignored { + Valid: boolean; + + static createFrom(source: any = {}) { + return new Ignored(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Valid = source["Valid"]; + } + } + + } +`, +} diff --git a/v2/internal/binding/binding_test/binding_importedmap_test.go b/v2/internal/binding/binding_test/binding_importedmap_test.go index 7fa11d54b0e..4a4b2996c18 100644 --- a/v2/internal/binding/binding_test/binding_importedmap_test.go +++ b/v2/internal/binding/binding_test/binding_importedmap_test.go @@ -50,7 +50,7 @@ export namespace binding_test { export namespace binding_test_import { export class AMapWrapper { - AMap: {[key: string]: binding_test_nestedimport.A}; + AMap: Record; static createFrom(source: any = {}) { return new AMapWrapper(source); } diff --git a/v2/internal/binding/binding_test/binding_nonstringmapkey_test.go b/v2/internal/binding/binding_test/binding_nonstringmapkey_test.go index 37a61dd2963..9efee710ff3 100644 --- a/v2/internal/binding/binding_test/binding_nonstringmapkey_test.go +++ b/v2/internal/binding/binding_test/binding_nonstringmapkey_test.go @@ -18,7 +18,7 @@ var NonStringMapKeyTest = BindingTest{ want: ` export namespace binding_test { export class NonStringMapKey { - numberMap: {[key: number]: any}; + numberMap: Record; static createFrom(source: any = {}) { return new NonStringMapKey(source); } diff --git a/v2/internal/binding/binding_test/binding_test.go b/v2/internal/binding/binding_test/binding_test.go index 32ebbe05663..d358d8b0f38 100644 --- a/v2/internal/binding/binding_test/binding_test.go +++ b/v2/internal/binding/binding_test/binding_test.go @@ -51,6 +51,10 @@ func TestBindings_GenerateModels(t *testing.T) { SpecialCharacterFieldTest, WithoutFieldsTest, NoFieldTagsTest, + Generics1Test, + Generics2Test, + IgnoredTest, + DeepElementsTest, } testLogger := &logger.Logger{} diff --git a/v2/internal/binding/binding_test/binding_type_alias_test.go b/v2/internal/binding/binding_test/binding_type_alias_test.go index 498c5976ceb..90b009c5f6c 100644 --- a/v2/internal/binding/binding_test/binding_type_alias_test.go +++ b/v2/internal/binding/binding_test/binding_type_alias_test.go @@ -15,11 +15,11 @@ const expectedTypeAliasBindings = `// Cynhyrchwyd y ffeil hon yn awtomatig. PEID import {binding_test} from '../models'; import {int_package} from '../models'; -export function Map():Promise<{[key: string]: string}>; +export function Map():Promise>; export function MapAlias():Promise; -export function MapWithImportedStructValue():Promise<{[key: string]: int_package.SomeStruct}>; +export function MapWithImportedStructValue():Promise>; export function Slice():Promise>; diff --git a/v2/internal/binding/generate.go b/v2/internal/binding/generate.go index 02a0bd29272..77edc983d76 100644 --- a/v2/internal/binding/generate.go +++ b/v2/internal/binding/generate.go @@ -171,7 +171,18 @@ func fullyQualifiedName(packageName string, typeName string) string { } } +var ( + jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`) +) + func arrayifyValue(valueArray string, valueType string) string { + valueType = strings.ReplaceAll(valueType, "*", "") + gidx := strings.IndexRune(valueType, '[') + if gidx > 0 { // its a generic type + rem := strings.SplitN(valueType, "[", 2) + valueType = rem[0] + "_" + jsVariableUnsafeChars.ReplaceAllLiteralString(rem[1], "_") + } + if len(valueArray) == 0 { return valueType } @@ -217,7 +228,7 @@ func goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) stri } if len(key) > 0 { - return fmt.Sprintf("{[key: %s]: %s}", key, arrayifyValue(valueArray, value)) + return fmt.Sprintf("Record<%s, %s>", key, arrayifyValue(valueArray, value)) } return arrayifyValue(valueArray, value) diff --git a/v2/internal/binding/generate_test.go b/v2/internal/binding/generate_test.go index 8d6a833b817..26d7c70dfe2 100644 --- a/v2/internal/binding/generate_test.go +++ b/v2/internal/binding/generate_test.go @@ -116,18 +116,28 @@ func Test_goTypeToJSDocType(t *testing.T) { { name: "map", input: "map[string]float64", - want: "{[key: string]: number}", + want: "Record", }, { name: "map", input: "map[string]map[string]float64", - want: "{[key: string]: {[key: string]: number}}", + want: "Record>", }, { name: "types", input: "main.SomeType", want: "main.SomeType", }, + { + name: "primitive_generic", + input: "main.ListData[string]", + want: "main.ListData_string_", + }, + { + name: "stdlib_generic", + input: "main.ListData[*net/http.Request]", + want: "main.ListData_net_http_Request_", + }, } var importNamespaces slicer.StringSlicer for _, tt := range tests { diff --git a/v2/internal/binding/reflect.go b/v2/internal/binding/reflect.go index 57a6335bd5b..d293a743aa2 100644 --- a/v2/internal/binding/reflect.go +++ b/v2/internal/binding/reflect.go @@ -166,7 +166,7 @@ func getPackageName(in string) string { } func getSplitReturn(in string) (string, string) { - result := strings.Split(in, ".") + result := strings.SplitN(in, ".", 2) return result[0], result[1] } diff --git a/v2/internal/typescriptify/typescriptify.go b/v2/internal/typescriptify/typescriptify.go index f8cb14838eb..95376b2f4f8 100644 --- a/v2/internal/typescriptify/typescriptify.go +++ b/v2/internal/typescriptify/typescriptify.go @@ -40,6 +40,20 @@ const ( jsVariableNameRegex = `^([A-Z]|[a-z]|\$|_)([A-Z]|[a-z]|[0-9]|\$|_)*$` ) +var ( + jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`) +) + +func nameTypeOf(typeOf reflect.Type) string { + tname := typeOf.Name() + gidx := strings.IndexRune(tname, '[') + if gidx > 0 { // its a generic type + rem := strings.SplitN(tname, "[", 2) + tname = rem[0] + "_" + jsVariableUnsafeChars.ReplaceAllLiteralString(rem[1], "_") + } + return tname +} + // TypeOptions overrides options set by `ts_*` tags. type TypeOptions struct { TSType string @@ -261,15 +275,32 @@ func (t *TypeScriptify) AddType(typeOf reflect.Type) *TypeScriptify { func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.StructField) { keyType := field.Type.Key() valueType := field.Type.Elem() - valueTypeName := valueType.Name() - if name, ok := t.types[valueType.Kind()]; ok { - valueTypeName = name + valueTypeName := nameTypeOf(valueType) + valueTypeSuffix := "" + if valueType.Kind() == reflect.Ptr { + valueType = valueType.Elem() + valueTypeName = nameTypeOf(valueType) } if valueType.Kind() == reflect.Array || valueType.Kind() == reflect.Slice { - valueTypeName = valueType.Elem().Name() + "[]" + arrayDepth := 1 + for valueType.Elem().Kind() == reflect.Array || valueType.Elem().Kind() == reflect.Slice { + valueType = valueType.Elem() + arrayDepth++ + } + valueType = valueType.Elem() + valueTypeName = nameTypeOf(valueType) + valueTypeSuffix = strings.Repeat("[]", arrayDepth) } if valueType.Kind() == reflect.Ptr { - valueTypeName = valueType.Elem().Name() + valueType = valueType.Elem() + valueTypeName = nameTypeOf(valueType) + } + if name, ok := t.types[valueType.Kind()]; ok { + valueTypeName = name + } + if valueType.Kind() == reflect.Map { + // TODO: support nested maps + valueTypeName = "any" // valueType.Elem().Name() } if valueType.Kind() == reflect.Struct && differentNamespaces(t.namespace, valueType) { valueTypeName = valueType.String() @@ -294,11 +325,13 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str fieldName = fmt.Sprintf(`"%s"?`, strippedFieldName) } } - t.fields = append(t.fields, fmt.Sprintf("%s%s: {[key: %s]: %s};", t.indent, fieldName, keyTypeStr, valueTypeName)) + t.fields = append(t.fields, fmt.Sprintf("%s%s: Record<%s, %s>;", t.indent, fieldName, keyTypeStr, valueTypeName+valueTypeSuffix)) if valueType.Kind() == reflect.Struct { - t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = this.convertValues(source[\"%s\"], %s, true);", t.indent, t.indent, dotField, strippedFieldName, t.prefix+valueTypeName+t.suffix)) + t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = this.convertValues(source[\"%s\"], %s, true);", + t.indent, t.indent, dotField, strippedFieldName, t.prefix+valueTypeName+valueTypeSuffix+t.suffix)) } else { - t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = source[\"%s\"];", t.indent, t.indent, dotField, strippedFieldName)) + t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = source[\"%s\"];", + t.indent, t.indent, dotField, strippedFieldName)) } } @@ -501,7 +534,7 @@ func (t *TypeScriptify) convertEnum(depth int, typeOf reflect.Type, elements []e } t.alreadyConverted[typeOf.String()] = true - entityName := t.Prefix + typeOf.Name() + t.Suffix + entityName := t.Prefix + nameTypeOf(typeOf) + t.Suffix result := "enum " + entityName + " {\n" for _, val := range elements { @@ -607,7 +640,7 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m t.alreadyConverted[typeOf.String()] = true - entityName := t.Prefix + typeOf.Name() + t.Suffix + entityName := t.Prefix + nameTypeOf(typeOf) + t.Suffix if typeClashWithReservedKeyword(entityName) { warnAboutTypesClash(entityName) @@ -667,8 +700,10 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m } isKnownType := t.KnownStructs.Contains(getStructFQN(field.Type.String())) - println("KnownStructs:", t.KnownStructs.Join("\t")) - println(getStructFQN(field.Type.String())) + if !isKnownType { + println("KnownStructs:", t.KnownStructs.Join("\t")) + println("Not found:", getStructFQN(field.Type.String())) + } builder.AddStructField(jsonFieldName, field, !isKnownType) } else if field.Type.Kind() == reflect.Map { t.logf(depth, "- map field %s.%s", typeOf.Name(), field.Name) @@ -714,11 +749,15 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m } arrayDepth := 1 - for field.Type.Elem().Kind() == reflect.Slice { // Slice of slices: + for field.Type.Elem().Kind() == reflect.Slice || field.Type.Elem().Kind() == reflect.Array { // Slice of slices: field.Type = field.Type.Elem() arrayDepth++ } + if field.Type.Elem().Kind() == reflect.Ptr { // extract ptr type + field.Type = field.Type.Elem() + } + if field.Type.Elem().Kind() == reflect.Struct { // Slice of structs: t.logf(depth, "- struct slice %s.%s (%s)", typeOf.Name(), field.Name, field.Type.String()) typeScriptChunk, err := t.convertType(depth+1, field.Type.Elem(), customCode) @@ -808,8 +847,12 @@ type typeScriptClassBuilder struct { } func (t *typeScriptClassBuilder) AddSimpleArrayField(fieldName string, field reflect.StructField, arrayDepth int, opts TypeOptions) error { - fieldType, kind := field.Type.Elem().Name(), field.Type.Elem().Kind() - typeScriptType := t.types[kind] + fieldType := nameTypeOf(field.Type.Elem()) + kind := field.Type.Elem().Kind() + typeScriptType, ok := t.types[kind] + if !ok { + typeScriptType = "any" + } if len(fieldName) > 0 { strippedFieldName := strings.ReplaceAll(fieldName, "?", "") @@ -828,9 +871,14 @@ func (t *typeScriptClassBuilder) AddSimpleArrayField(fieldName string, field ref } func (t *typeScriptClassBuilder) AddSimpleField(fieldName string, field reflect.StructField, opts TypeOptions) error { - fieldType, kind := field.Type.Name(), field.Type.Kind() + fieldType := nameTypeOf(field.Type) + kind := field.Type.Kind() + + typeScriptType, ok := t.types[kind] + if !ok { + typeScriptType = "any" + } - typeScriptType := t.types[kind] if len(opts.TSType) > 0 { typeScriptType = opts.TSType } @@ -852,7 +900,7 @@ func (t *typeScriptClassBuilder) AddSimpleField(fieldName string, field reflect. } func (t *typeScriptClassBuilder) AddEnumField(fieldName string, field reflect.StructField) { - fieldType := field.Type.Name() + fieldType := nameTypeOf(field.Type) t.addField(fieldName, t.prefix+fieldType+t.suffix, false) strippedFieldName := strings.ReplaceAll(fieldName, "?", "") t.addInitializerFieldLine(strippedFieldName, fmt.Sprintf("source[\"%s\"]", strippedFieldName)) @@ -862,7 +910,7 @@ func (t *typeScriptClassBuilder) AddStructField(fieldName string, field reflect. strippedFieldName := strings.ReplaceAll(fieldName, "?", "") classname := "null" namespace := strings.Split(field.Type.String(), ".")[0] - fqname := t.prefix + field.Type.Name() + t.suffix + fqname := t.prefix + nameTypeOf(field.Type) + t.suffix if namespace != t.namespace { fqname = namespace + "." + fqname } @@ -881,7 +929,7 @@ func (t *typeScriptClassBuilder) AddStructField(fieldName string, field reflect. } func (t *typeScriptClassBuilder) AddArrayOfStructsField(fieldName string, field reflect.StructField, arrayDepth int) { - fieldType := field.Type.Elem().Name() + fieldType := nameTypeOf(field.Type.Elem()) if differentNamespaces(t.namespace, field.Type.Elem()) { fieldType = field.Type.Elem().String() } diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 2e148afb4e4..9ba4eb43d44 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - chore: fix some comments in [PR](https://github.com/wailsapp/wails/pull/3932) by @lvyaoting - [windows] Fixed frameless window flickering when minimizing/restoring by preventing unnecessary redraws [#3951](https://github.com/wailsapp/wails/issues/3951) - Fixed failed models.ts build due to non-json-encodable Go types [PR](https://github.com/wailsapp/wails/pull/3975) by [@pbnjay](https://github.com/pbnjay) +- Fixed more binding and typescript export bugs [PR](https://github.com/wailsapp/wails/pull/3978) by [@pbnjay](https://github.com/pbnjay) ### Changed - Allow to specify macos-min-version externally. Implemented by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/3756)