From 8b1c7c25a75221d075de4839febb0c325e37f58c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?=
 <miku1958@users.noreply.github.com>
Date: Sat, 11 May 2024 18:10:39 +0800
Subject: [PATCH] fix return type become tuple type from interface type (#124)

* fix return type become tuple type from interface type

* bump to 0.12.1
---
 package-lock.json         |  4 ++--
 package.json              |  2 +-
 src/parser/ValueParser.ts | 38 +++++++++++++++-----------------------
 test/value-parser-test.ts | 39 +++++++++++++++++++++++++++++++++++++--
 4 files changed, 55 insertions(+), 28 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 9ece0e5..a116e2e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "ts-gyb",
-  "version": "0.12.0",
+  "version": "0.12.1",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "ts-gyb",
-      "version": "0.12.0",
+      "version": "0.12.1",
       "license": "MIT",
       "dependencies": {
         "chalk": "^4.1.1",
diff --git a/package.json b/package.json
index 225306b..4a54ac5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "ts-gyb",
-  "version": "0.12.0",
+  "version": "0.12.1",
   "description": "Generate Native API based on TS interface",
   "repository": {
     "type": "git",
diff --git a/src/parser/ValueParser.ts b/src/parser/ValueParser.ts
index e3058e0..681c7f5 100644
--- a/src/parser/ValueParser.ts
+++ b/src/parser/ValueParser.ts
@@ -267,9 +267,9 @@ export class ValueParser {
     }
 
     let nullable = false;
-    let isTuple = false;
-    const valueTypes: ValueType[] = [];
-    const tupleMembers: Field[] = [];
+    const valueTypes: NonEmptyType[] = [];
+    let hasTupleType = false;
+    const tupleOrInterfaceTypes: (TupleType | InterfaceType)[] = [];
     const literalValues: {
       type: BasicTypeValue.string | BasicTypeValue.number;
       value: Value;
@@ -295,23 +295,15 @@ export class ValueParser {
         return;
       }
 
-      const newValueType = this.valueTypeFromTypeNode(typeNode);
+      const newValueType = this.valueTypeFromTypeNode(typeNode) as NonEmptyType;
 
-      if (valueTypes.length === 0) {
-        isTuple = isInterfaceType(newValueType) || isTupleType(newValueType);
+      valueTypes.push(newValueType);
+      if (isTupleType(newValueType)) {
+        hasTupleType = true;
+        tupleOrInterfaceTypes.push(newValueType);
       }
-
-      if (isTuple) {
-        if (isInterfaceType(newValueType) || isTupleType(newValueType)) {
-          tupleMembers.push(...newValueType.members);
-        } else {
-          throw new ValueParserError(
-            `type ${node.getText()} is invalid`,
-            'Use `multiple tuple types` or `union value types` or `type union`'
-          );
-        }
-      } else {
-        valueTypes.push(newValueType);
+      if (isInterfaceType(newValueType)) {
+        tupleOrInterfaceTypes.push(newValueType);
       }
     });
 
@@ -348,24 +340,24 @@ export class ValueParser {
       return literalKind;
     }
 
-    if (valueTypes.length === 0 && tupleMembers.length === 0) {
+    if (valueTypes.length === 0) {
       throw new ValueParserError(
         `type ${node.getText()} is invalid`,
         'Type must contain one supported non empty type'
       );
     }
 
-    if (valueTypes.length > 0 && tupleMembers.length > 0) {
+    if (hasTupleType && valueTypes.length !== tupleOrInterfaceTypes.length) {
       throw new ValueParserError(
         `mixing ${node.getText()} is invalid`,
         'Type must contain types or tuples'
       );
     }
 
-    if (isTuple) {
+    if (hasTupleType) {
       const value: TupleType = {
         kind: ValueTypeKind.tupleType,
-        members: tupleMembers,
+        members: tupleOrInterfaceTypes.map((type) => type.members).flat(),
       };
       if (nullable) {
         const optionalType: OptionalType = {
@@ -378,7 +370,7 @@ export class ValueParser {
     }
 
     if (valueTypes.length === 1) {
-      if (!isOptionalType(valueTypes[0]) && nullable) {
+      if (nullable) {
         return {
           kind: ValueTypeKind.optionalType,
           wrappedType: valueTypes[0],
diff --git a/test/value-parser-test.ts b/test/value-parser-test.ts
index 9ba1ce5..1f1773f 100644
--- a/test/value-parser-test.ts
+++ b/test/value-parser-test.ts
@@ -32,6 +32,42 @@ describe('ValueParser', () => {
         expect(parseFunc()).to.deep.equal({ name: 'mockedMethod', parameters: [], returnType: null, isAsync: true, documentation: '', });
       })
     });
+
+    it('Return type', () => {
+      let customTypesCode = `
+      export interface SelectionRect {
+        start: number;
+      }
+      `
+      const methodCode = `
+      mockedMethod(): SelectionRect;
+      `;
+      withTempMethodParser(methodCode, parseFunc => {
+        expect(parseFunc()).to.deep.equal({
+          name: 'mockedMethod',
+          parameters: [],
+          returnType: {
+            name: 'SelectionRect',
+            customTags: {},
+            methods: [],
+            documentation: '',
+            kind: 'interfaceType',
+            members: [
+              {
+                documentation: '',
+                name: 'start',
+                type: {
+                  kind: 'basicType',
+                  value: 'number',
+                },
+              },
+            ],
+          },
+          isAsync: false,
+          documentation: '',
+        });
+      }, new Set(), customTypesCode)
+    });
   });
 
   describe('Parameters type', () => {
@@ -184,7 +220,6 @@ describe('ValueParser', () => {
     }
     `;
     testValueType('merged interface and tuple', 'InterfaceWithStringField | { numberField: number }', tupleWithMembersType, new Set(), interfacesCode);
-    testValueType('merged interfaces to tuple', 'InterfaceWithStringField | InterfaceWithNumberField', tupleWithMembersType, new Set(), interfacesCode);
   })
 
   describe('Parse enum type', () => {
@@ -287,7 +322,7 @@ describe('ValueParser', () => {
     testValueType('string interface dictionary', 'DictionaryInterface', { kind: ValueTypeKind.dictionaryType, keyType: DictionaryKeyType.string, valueType: numberType }, new Set(), dictionaryCode);
   });
 
-  describe('Parse optional type', () => {
+  describe('Parse union type', () => {
     it('Empty types union', () => {
       const valueTypeCode = 'null | undefined';
       withTempValueParser(valueTypeCode, parseFunc => {