openapi-zod-client icon indicating copy to clipboard operation
openapi-zod-client copied to clipboard

TypeScript error: Type is too long to infer, needs explicit type annotation.

Open wembleyoz opened this issue 1 year ago • 6 comments

Read before opening

  • Did you search the current list of issues ? Yes
  • Is your issue related to zod or zodios ? No
  • Are you using a Swagger v2 input schema ? No
  • Do you really need runtime validation ? Yes

Describe the bug When generating the client from a large OpenAPI schema, such as the one at LCU OpenAPI schema, the following TypeScript error is encountered: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.

The error seems to occur due to the size and complexity of the generated schema and type inference limits in TypeScript.

Minimal reproduction

Expected behavior I expect the generated code to either handle larger schemas gracefully or suggest adding explicit type annotations to avoid the TypeScript inference error. The code should look like the example provided, which avoids importing large objects directly and instead exports each schema individually.

Additional context

  • Using OpenAPI 3.0 schema from LCU OpenAPI schema.
  • The issue occurs with large schemas and complex type inference in TypeScript. The error suggests using explicit type annotations for better control over the type size.

Suggested improvement Instead of importing large schemas as a whole (schemas = {...all_schemas}), export individual schemas to avoid the same issue with TypeScript’s type inference limits.

Schematic example of the suggested code change:

import { makeApi, Zodios, type ZodiosOptions, ZodiosInstance } from "@zodios/core";
import { z } from "zod";

{{#if imports}}
{{#each imports}}
import { {{{@key}}} } from "./{{{this}}}"
{{/each}}
{{/if}}

{{#if types}}
{{#each types}}
{{{this}}};
{{/each}}
{{/if}}

{{#ifNotEmptyObj schemas}}
{{#each schemas}}
export const {{@key}}{{#if (lookup ../emittedType @key)}}: z.ZodType<{{@key}}>{{/if}} = {{{this}}};
{{/each}}
{{/ifNotEmptyObj}}

const endpoints = makeApi([
{{#each endpoints}}
	{
		method: "{{method}}",
		path: "{{path}}",
		{{#if @root.options.withAlias}}
		{{#if alias}}
		alias: "{{alias}}",
		{{/if}}
		{{/if}}
		{{#if description}}
		description: `{{description}}`,
		{{/if}}
		{{#if requestFormat}}
		requestFormat: "{{requestFormat}}",
		{{/if}}
		{{#if parameters}}
		parameters: [
			{
				name: "{{name}}",
				{{#if description}}
				description: `{{description}}`,
				{{/if}}
				type: "{{type}}",
				schema: {{{schema}}}
			},
			{{/each}}
		],
		response: {{{response}}},
		errors: [
			{
				status: {{status}},
				description: `{{description}}`,
				schema: {{{schema}}}
			}
		]
	},
{{/each}}
]);

export const apiClient: ZodiosInstance<typeof endpoints> = new Zodios(endpoints);

export function createApiClient(baseUrl: string, options?: ZodiosOptions) {
    return new Zodios(baseUrl, endpoints, options);
}

wembleyoz avatar Sep 19 '24 09:09 wembleyoz

We are also running in to this issue. Tried the above but we also get the errors in the createApiClient function and the const endpoints = makeApi.

Our only workaround I think right now is to group the endpoints. Any other guidance would be appreciated.

ChrisSargent avatar Nov 07 '24 11:11 ChrisSargent

We are also running in to this issue. Tried the above but we also get the errors in the createApiClient function and the const endpoints = makeApi.

Our only workaround I think right now is to group the endpoints. Any other guidance would be appreciated.

You can just replace the variable apiClient with

export const apiClient: ZodiosInstance<typeof endpoints> = new Zodios(endpoints);

wembleyoz avatar Nov 14 '24 11:11 wembleyoz

We are also running in to this issue. Tried the above but we also get the errors in the createApiClient function and the const endpoints = makeApi. Our only workaround I think right now is to group the endpoints. Any other guidance would be appreciated.

You can just replace the variable apiClient with

export const apiClient: ZodiosInstance<typeof endpoints> = new Zodios(endpoints);

not for us, it just shifts the same error to the const endpoints line....

ChrisSargent avatar Nov 14 '24 16:11 ChrisSargent

Same for us, will this be possible to fix without splitting up APIs or is it a limitation of TypeScript itself?

alden12 avatar Nov 19 '24 16:11 alden12

Same issue for large openapi spec.

I had to create a post transformation script.

#!/usr/bin/env node

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

function transformSchemasFile(filePath) {
  console.log(`Transforming schemas in ${filePath}...`);
  
  let content = fs.readFileSync(filePath, 'utf8');
  
  // Add export to all const declarations
  content = content.replace(/^const /gm, 'export const ');
  
  // Remove the schemas object if it exists
  content = content.replace(/export const schemas = \{([^}]+)\};/s, '');
  
  fs.writeFileSync(filePath, content);
  console.log(`✅ Added exports to all consts and removed schemas object from ${filePath}`);
}

// Transform API files
const v1Path = path.join(__dirname, '../src/shared/generated/v1/index.ts');

if (fs.existsSync(v1Path)) {
  transformSchemasFile(v1Path);
} else {
  console.log('⚠️  V1 API file not found, skipping...');
}

console.log('🎉 Schema transformation complete!'); 

grant avatar Jul 22 '25 19:07 grant

In fact, any of those solutions works when you have very nested types or recursive types. I believe the solution is a mix between generating the types in TypeScript and then generating the schemas like

export const myZodSchemaX = z.ZodType<MyPregeneratedType> : zodDefinition;

mbsanchez01 avatar Aug 28 '25 15:08 mbsanchez01