diff --git a/v2/internal/binding/binding_test/binding_anonymous_sub_struct_multi_level_test.go b/v2/internal/binding/binding_test/binding_anonymous_sub_struct_multi_level_test.go new file mode 100644 index 000000000..3c888ab27 --- /dev/null +++ b/v2/internal/binding/binding_test/binding_anonymous_sub_struct_multi_level_test.go @@ -0,0 +1,65 @@ +package binding_test + +type StructWithAnonymousSubMultiLevelStruct struct { + Name string `json:"name"` + Meta struct { + Age int `json:"age"` + More struct { + Info string `json:"info"` + MoreInMore struct { + Demo string `json:"demo"` + } `json:"more_in_more"` + } `json:"more"` + } `json:"meta"` +} + +func (s StructWithAnonymousSubMultiLevelStruct) Get() StructWithAnonymousSubMultiLevelStruct { + return s +} + +var AnonymousSubStructMultiLevelTest = BindingTest{ + name: "StructWithAnonymousSubMultiLevelStruct", + structs: []interface{}{ + &StructWithAnonymousSubMultiLevelStruct{}, + }, + exemptions: nil, + shouldError: false, + want: ` +export namespace binding_test { + export class StructWithAnonymousSubMultiLevelStruct { + name: string; + // Go type: struct { Age int "json:\"age\""; More struct { Info string "json:\"info\""; MoreInMore struct { Demo string "json:\"demo\"" } "json:\"more_in_more\"" } "json:\"more\"" } + meta: any; + + static createFrom(source: any = {}) { + return new StructWithAnonymousSubMultiLevelStruct(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.meta = this.convertValues(source["meta"], Object); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + 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_anonymous_sub_struct_test.go b/v2/internal/binding/binding_test/binding_anonymous_sub_struct_test.go new file mode 100644 index 000000000..53617efac --- /dev/null +++ b/v2/internal/binding/binding_test/binding_anonymous_sub_struct_test.go @@ -0,0 +1,59 @@ +package binding_test + +type StructWithAnonymousSubStruct struct { + Name string `json:"name"` + Meta struct { + Age int `json:"age"` + } `json:"meta"` +} + +func (s StructWithAnonymousSubStruct) Get() StructWithAnonymousSubStruct { + return s +} + +var AnonymousSubStructTest = BindingTest{ + name: "StructWithAnonymousSubStruct", + structs: []interface{}{ + &StructWithAnonymousSubStruct{}, + }, + exemptions: nil, + shouldError: false, + want: ` +export namespace binding_test { + export class StructWithAnonymousSubStruct { + name: string; + // Go type: struct { Age int "json:\"age\"" } + meta: any; + + static createFrom(source: any = {}) { + return new StructWithAnonymousSubStruct(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.meta = this.convertValues(source["meta"], Object); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + 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_emptystruct_test.go b/v2/internal/binding/binding_test/binding_emptystruct_test.go index 46ff69adc..c36603e64 100644 --- a/v2/internal/binding/binding_test/binding_emptystruct_test.go +++ b/v2/internal/binding/binding_test/binding_emptystruct_test.go @@ -27,7 +27,7 @@ export namespace binding_test { } constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.empty = this.convertValues(source["empty"], null); + this.empty = this.convertValues(source["empty"], Object); } convertValues(a: any, classs: any, asMap: boolean = false): any { if (!a) { diff --git a/v2/internal/binding/binding_test/binding_test.go b/v2/internal/binding/binding_test/binding_test.go index f3d3a6611..0b143db17 100644 --- a/v2/internal/binding/binding_test/binding_test.go +++ b/v2/internal/binding/binding_test/binding_test.go @@ -37,6 +37,8 @@ func TestBindings_GenerateModels(t *testing.T) { MultistructTest, EmptyStructTest, GeneratedJsEntityTest, + AnonymousSubStructTest, + AnonymousSubStructMultiLevelTest, } testLogger := &logger.Logger{} diff --git a/v2/internal/typescriptify/typescriptify.go b/v2/internal/typescriptify/typescriptify.go index dfc77ef29..c30b6bf83 100644 --- a/v2/internal/typescriptify/typescriptify.go +++ b/v2/internal/typescriptify/typescriptify.go @@ -641,13 +641,19 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m err = builder.AddSimpleField(jsonFieldName, field, fldOpts) } else if field.Type.Kind() == reflect.Struct { // Struct: t.logf(depth, "- struct %s.%s (%s)", typeOf.Name(), field.Name, field.Type.String()) - typeScriptChunk, err := t.convertType(depth+1, field.Type, customCode) - if err != nil { - return "", err - } - if typeScriptChunk != "" { - result = typeScriptChunk + "\n" + result + + // Anonymous structures is ignored + // It is possible to generate them but hard to generate correct name + if field.Type.Name() != "" { + typeScriptChunk, err := t.convertType(depth+1, field.Type, customCode) + if err != nil { + return "", err + } + if typeScriptChunk != "" { + result = typeScriptChunk + "\n" + result + } } + isKnownType := t.KnownStructs.Contains(getStructFQN(field.Type.String())) println("KnownStructs:", t.KnownStructs.Join("\t")) println(getStructFQN(field.Type.String())) @@ -833,16 +839,24 @@ func (t *typeScriptClassBuilder) AddEnumField(fieldName string, field reflect.St func (t *typeScriptClassBuilder) AddStructField(fieldName string, field reflect.StructField, isAnyType bool) { strippedFieldName := strings.ReplaceAll(fieldName, "?", "") - namespace := strings.Split(field.Type.String(), ".")[0] - fqname := "any" + fqname := field.Type.Name() classname := "null" - fqname = field.Type.Name() + + namespace := strings.Split(field.Type.String(), ".")[0] + if namespace != t.namespace { fqname = field.Type.String() } + if !isAnyType { classname = fqname } + + // Anonymous struct + if field.Type.Name() == "" { + classname = "Object" + } + t.addField(fieldName, fqname, isAnyType) t.addInitializerFieldLine(strippedFieldName, fmt.Sprintf("this.convertValues(source[\"%s\"], %s)", strippedFieldName, classname)) }