Skip to content

50+ endpoints and typescript composite yields to TS7056 error. #511

Description

@PeterMK85

Versions

   "@zodios/core": "10.9.4",
   "zod": "3.22.1"

Number of Endpoints: 73
Number of Schemas: 403

I tried the 11.0.0-beta.19 version, failed with the same.

Context

We are trying to switch from an open api -> axios setup (used swagger-typescript-api) into a open api -> zodios setup (used openapi-zod-client).

We also using the composite flag from typescript in the monorepo, to improve typescript performance.

Issue

I use the example as a base, to illustrate the possible rootcause, the actual contract file I can provide on request but it seems not related to the actual setup, it's more about the complexity / amount of the endpoints.

Actual TS error:

The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.

The issue reported from the following approaches
new Zodios(baseUrl, endpoints, options)
and the
makeApi(endpoints)

If I start to reduce the number of endpoint and shared schemas it will start to pass.

Simple example:

Schemas + client
import { Zodios, type ZodiosOptions } from '@zodios/core'
import { z } from 'zod'

export const Category = z.object({ id: z.number().int(), name: z.string() }).partial()
export type Category = z.infer<typeof Category>
export const Tag = z.object({ id: z.number().int(), name: z.string() }).partial()
export type Tag = z.infer<typeof Tag>
export const Pet = z.object({
    id: z.number().int().optional(),
    name: z.string(),
    category: Category.optional(),
    photoUrls: z.array(z.string()),
    tags: z.array(Tag).optional(),
    status: z.enum(['available', 'pending', 'sold']).optional(),
})
export type Pet = z.infer<typeof Pet>
export const createApiClient = (baseUrl: string, options?: ZodiosOptions) =>
    new Zodios(
        baseUrl,
        [
            {
                method: 'put',
                path: '/pet',
                alias: 'updatepet',
                description: `Update an existing pet by Id`,
                requestFormat: 'json',
                parameters: [
                    {
                        name: 'body',
                        description: `Update an existent pet in the store`,
                        type: 'Body',
                        schema: Pet,
                    },
                ],
                response: Pet,
                errors: [
                    {
                        status: 400,
                        description: `Invalid ID supplied`,
                        schema: z.void(),
                    },
                    {
                        status: 404,
                        description: `Pet not found`,
                        schema: z.void(),
                    },
                    {
                        status: 405,
                        description: `Validation exception`,
                        schema: z.void(),
                    },
                ],
            },
        ],
        options,
    )
Typescript d.ts file output
import { type ZodiosOptions } from '@zodios/core';
import { z } from 'zod';
export declare const Category: z.ZodObject<{
    id: z.ZodOptional<z.ZodNumber>;
    name: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
    id?: number | undefined;
    name?: string | undefined;
}, {
    id?: number | undefined;
    name?: string | undefined;
}>;
export type Category = z.infer<typeof Category>;
export declare const Tag: z.ZodObject<{
    id: z.ZodOptional<z.ZodNumber>;
    name: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
    id?: number | undefined;
    name?: string | undefined;
}, {
    id?: number | undefined;
    name?: string | undefined;
}>;
export type Tag = z.infer<typeof Tag>;
export declare const Pet: z.ZodObject<{
    id: z.ZodOptional<z.ZodNumber>;
    name: z.ZodString;
    category: z.ZodOptional<z.ZodObject<{
        id: z.ZodOptional<z.ZodNumber>;
        name: z.ZodOptional<z.ZodString>;
    }, "strip", z.ZodTypeAny, {
        id?: number | undefined;
        name?: string | undefined;
    }, {
        id?: number | undefined;
        name?: string | undefined;
    }>>;
    photoUrls: z.ZodArray<z.ZodString, "many">;
    tags: z.ZodOptional<z.ZodArray<z.ZodObject<{
        id: z.ZodOptional<z.ZodNumber>;
        name: z.ZodOptional<z.ZodString>;
    }, "strip", z.ZodTypeAny, {
        id?: number | undefined;
        name?: string | undefined;
    }, {
        id?: number | undefined;
        name?: string | undefined;
    }>, "many">>;
    status: z.ZodOptional<z.ZodEnum<["available", "pending", "sold"]>>;
}, "strip", z.ZodTypeAny, {
    name: string;
    photoUrls: string[];
    id?: number | undefined;
    category?: {
        id?: number | undefined;
        name?: string | undefined;
    } | undefined;
    tags?: {
        id?: number | undefined;
        name?: string | undefined;
    }[] | undefined;
    status?: "available" | "pending" | "sold" | undefined;
}, {
    name: string;
    photoUrls: string[];
    id?: number | undefined;
    category?: {
        id?: number | undefined;
        name?: string | undefined;
    } | undefined;
    tags?: {
        id?: number | undefined;
        name?: string | undefined;
    }[] | undefined;
    status?: "available" | "pending" | "sold" | undefined;
}>;
export type Pet = z.infer<typeof Pet>;
export declare const createApiClient: (baseUrl: string, options?: ZodiosOptions) => import("@zodios/core").ZodiosInstance<[{
    method: "put";
    path: "/pet";
    alias: "updatepet";
    description: "Update an existing pet by Id";
    requestFormat: "json";
    parameters: [{
        name: "body";
        description: string;
        type: "Body";
        schema: z.ZodObject<{
            id: z.ZodOptional<z.ZodNumber>;
            name: z.ZodString;
            category: z.ZodOptional<z.ZodObject<{
                id: z.ZodOptional<z.ZodNumber>;
                name: z.ZodOptional<z.ZodString>;
            }, "strip", z.ZodTypeAny, {
                id?: number | undefined;
                name?: string | undefined;
            }, {
                id?: number | undefined;
                name?: string | undefined;
            }>>;
            photoUrls: z.ZodArray<z.ZodString, "many">;
            tags: z.ZodOptional<z.ZodArray<z.ZodObject<{
                id: z.ZodOptional<z.ZodNumber>;
                name: z.ZodOptional<z.ZodString>;
            }, "strip", z.ZodTypeAny, {
                id?: number | undefined;
                name?: string | undefined;
            }, {
                id?: number | undefined;
                name?: string | undefined;
            }>, "many">>;
            status: z.ZodOptional<z.ZodEnum<["available", "pending", "sold"]>>;
        }, "strip", z.ZodTypeAny, {
            name: string;
            photoUrls: string[];
            id?: number | undefined;
            category?: {
                id?: number | undefined;
                name?: string | undefined;
            } | undefined;
            tags?: {
                id?: number | undefined;
                name?: string | undefined;
            }[] | undefined;
            status?: "available" | "pending" | "sold" | undefined;
        }, {
            name: string;
            photoUrls: string[];
            id?: number | undefined;
            category?: {
                id?: number | undefined;
                name?: string | undefined;
            } | undefined;
            tags?: {
                id?: number | undefined;
                name?: string | undefined;
            }[] | undefined;
            status?: "available" | "pending" | "sold" | undefined;
        }>;
    }];
    response: z.ZodObject<{
        id: z.ZodOptional<z.ZodNumber>;
        name: z.ZodString;
        category: z.ZodOptional<z.ZodObject<{
            id: z.ZodOptional<z.ZodNumber>;
            name: z.ZodOptional<z.ZodString>;
        }, "strip", z.ZodTypeAny, {
            id?: number | undefined;
            name?: string | undefined;
        }, {
            id?: number | undefined;
            name?: string | undefined;
        }>>;
        photoUrls: z.ZodArray<z.ZodString, "many">;
        tags: z.ZodOptional<z.ZodArray<z.ZodObject<{
            id: z.ZodOptional<z.ZodNumber>;
            name: z.ZodOptional<z.ZodString>;
        }, "strip", z.ZodTypeAny, {
            id?: number | undefined;
            name?: string | undefined;
        }, {
            id?: number | undefined;
            name?: string | undefined;
        }>, "many">>;
        status: z.ZodOptional<z.ZodEnum<["available", "pending", "sold"]>>;
    }, "strip", z.ZodTypeAny, {
        name: string;
        photoUrls: string[];
        id?: number | undefined;
        category?: {
            id?: number | undefined;
            name?: string | undefined;
        } | undefined;
        tags?: {
            id?: number | undefined;
            name?: string | undefined;
        }[] | undefined;
        status?: "available" | "pending" | "sold" | undefined;
    }, {
        name: string;
        photoUrls: string[];
        id?: number | undefined;
        category?: {
            id?: number | undefined;
            name?: string | undefined;
        } | undefined;
        tags?: {
            id?: number | undefined;
            name?: string | undefined;
        }[] | undefined;
        status?: "available" | "pending" | "sold" | undefined;
    }>;
    errors: [{
        status: 400;
        description: string;
        schema: z.ZodVoid;
    }, {
        status: 404;
        description: string;
        schema: z.ZodVoid;
    }, {
        status: 405;
        description: string;
        schema: z.ZodVoid;
    }];
}]>;

What I tried to illustrate above, that with a single endpoint and with very 3 simple schemas, typescript compilation produced 260% larger file, and it seems it tries to calculate the type of every schema at least twice. (schema declaration and the API response/body declaration. In comprehension the exported type, where I just rely on z.infer it preserves as a reference.

Actual fails stats:

With the very same structure as above:
Client + Schemas LOC: 5048
When I store every endpoint as a const and remove the makeApi/newZodios and TS success on compile, LOC: 432500

Do you have any suggestion? Do I missed something?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    wontfixThis will not be worked on

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions