Repository: fabien0102/ts-to-zod (ts)

Issue:
namespace + extends not working
## Bug description

When using `extends` inside `namespace`, there's an error:

```
// Without namespace, this works:
export interface BaseObject {
  "id": string;
}
export interface Object extends BaseObject {
  "name": string;
}

// With namespace, this will fail:
export namespace std {
  export interface BaseObject {
    "id": string;
  }
  export interface Object extends BaseObject {
    "name": string;
  }
}
```

Error with namespace:
```
 ›   Warning: Some schemas can't be generated due to circular dependencies:
 ›   stdObjectSchema
✔ Validating generated types
🎉 Zod schemas generated!
```

Only `BaseObject` get generated:
```ts
// Generated by ts-to-zod
import { z } from 'zod'

export const stdBaseObjectSchema = z.object({
  id: z.string(),
})
```

Without namepsace everything's good:
```ts
// Generated by ts-to-zod
import { z } from 'zod'

export const baseObjectSchema = z.object({
  id: z.string(),
})

export const objectSchema = baseObjectSchema.extend({
  name: z.string(),
})
```

**Versions**

ts-to-zod/1.13.1 wsl-x64 node-v19.3.0


Pull request:
fix: extends interface inside namespace
# Why

Fix extends interface inside a namespace

Closed #105 


Patch:
diff --git a/src/utils/resolveModules.ts b/src/utils/resolveModules.ts
index da976e4..53caeba 100644
--- a/src/utils/resolveModules.ts
+++ b/src/utils/resolveModules.ts
@@ -138,11 +138,33 @@ const moduleToPrefix = (
         node.modifiers,
         f.createIdentifier(pascal(moduleName) + pascal(node.name.text)),
         node.typeParameters,
-        node.heritageClauses,
+        ts.visitNodes(
+          node.heritageClauses,
+          prefixInterfacesAndTypes(moduleName)
+        ),
         ts.visitNodes(node.members, prefixInterfacesAndTypes(moduleName))
       );
     }
 
+    if (ts.isHeritageClause(node)) {
+      return f.updateHeritageClause(
+        node,
+        ts.visitNodes(node.types, prefixInterfacesAndTypes(moduleName))
+      );
+    }
+
+    if (
+      ts.isExpressionWithTypeArguments(node) &&
+      ts.isIdentifier(node.expression) &&
+      (declarationNames.get(moduleName) || []).includes(node.expression.text)
+    ) {
+      return f.updateExpressionWithTypeArguments(
+        node,
+        f.createIdentifier(pascal(moduleName) + pascal(node.expression.text)),
+        node.typeArguments
+      );
+    }
+
     if (ts.isEnumDeclaration(node)) {
       return f.updateEnumDeclaration(
         node,


Test patch:
diff --git a/src/utils/resolveModules.test.ts b/src/utils/resolveModules.test.ts
index bdb2875..01f3bb6 100644
--- a/src/utils/resolveModules.test.ts
+++ b/src/utils/resolveModules.test.ts
@@ -63,6 +63,10 @@ describe("resolveModules", () => {
         hasPower: boolean;
         weakness: Weakness;
       }
+
+      export interface Clark {
+        hasGlasses: boolean
+      }
       
       export type SupermanBis = {
         fullName: Name;
@@ -70,6 +74,10 @@ describe("resolveModules", () => {
         hasPower: boolean;
         weakness: Weakness;
       }
+
+      export interface ExtendedSuperman extends Superman, Clark {
+        hasEvenMorePower: boolean;
+      }
     }`;
 
     expect(print(resolveModules(sourceText))).toMatchInlineSnapshot(`
@@ -85,6 +93,9 @@ describe("resolveModules", () => {
           hasPower: boolean;
           weakness: Weakness;
       }
+      export interface MetropolisClark {
+          hasGlasses: boolean;
+      }
       export type MetropolisSupermanBis = {
           fullName: MetropolisName;
           name: {
@@ -94,6 +105,9 @@ describe("resolveModules", () => {
           hasPower: boolean;
           weakness: Weakness;
       };
+      export interface MetropolisExtendedSuperman extends MetropolisSuperman, MetropolisClark {
+          hasEvenMorePower: boolean;
+      }
       "
     `);
   });