Skip to content

[Bug]: z.lazy() in subgraph schema breaks TypeScript type inference requiring 'as any' casts #4650

@christian-byrne

Description

@christian-byrne

[Bug]: z.lazy() in subgraph schema breaks TypeScript type inference requiring 'as any' casts

Custom Node Testing

  • I have disabled all custom nodes and confirmed this bug is not related to custom nodes

ComfyUI Frontend Version

1.25.3+

Expected Behavior

When accessing graphData.definitions.subgraphs in TypeScript, the type system should correctly infer that each subgraph has properties like nodes, links, etc. (inherited from zComfyWorkflow1 via .extend()), without requiring as any[] type casting.

Actual Behavior

The z.lazy() wrapper in the subgraph schema definition breaks TypeScript's type inference. When iterating over graphData.definitions.subgraphs, TypeScript doesn't recognize the inherited properties, forcing developers to use as any[] to access properties like nodes:

// Current code requires 'as any[]'
for (const subgraph of graphData.definitions.subgraphs as any[]) {
  if (subgraph.nodes) {  // TypeScript doesn't know about 'nodes' without the cast
    processNodesRecursively(subgraph.nodes, subgraph.name || subgraph.id)
  }
}

Steps to Reproduce

  1. Open src/services/workflowService.ts or any file that processes workflow data
  2. Try to access graphData.definitions?.subgraphs and iterate over it
  3. Try to access .nodes property on a subgraph without type casting
  4. TypeScript will show error that property 'nodes' doesn't exist

Debug Logs

TypeScript error without the as any[] cast:

Property 'nodes' does not exist on type...

Browser Logs

N/A - This is a compile-time TypeScript issue

Setting JSON

N/A - This is a development/type system issue

Other Information

Root Cause:
The schema uses a complex z.lazy() wrapper to handle circular references (subgraphs can contain subgraphs):

definitions: z
  .object({
    subgraphs: z.lazy(
      (): z.ZodArray<
        z.ZodType<
          SubgraphDefinitionBase<ComfyWorkflow1BaseInput>,
          z.ZodTypeDef,
          SubgraphDefinitionBase<ComfyWorkflow1BaseInput>
        >,
        'many'
      > => zSubgraphDefinition.array()
    )
  })
  .optional()

The verbose generic type annotation in the lazy function appears to confuse TypeScript's type inference.

Why z.lazy() is needed:

  • zSubgraphDefinition extends zComfyWorkflow1
  • It can contain nested definitions.subgraphs (recursive structure)
  • Without z.lazy(), we get circular reference errors at the type level

Impact:

  • Developers must use as any[] throughout the codebase when working with subgraphs
  • Loss of type safety and IntelliSense for subgraph properties
  • Makes the code harder to maintain and refactor

Potential Solutions:

  1. Simplify the z.lazy() type annotation
  2. Create explicit helper types for better inference
  3. Use a different approach to handle the circular reference
  4. Add type assertion helpers that maintain safety while improving DX

┆Issue is synchronized with this Notion page by Unito

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions