Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overloaded function return schema with z.union #3592

Open
MikeDabrowski opened this issue Jun 21, 2024 · 0 comments
Open

Overloaded function return schema with z.union #3592

MikeDabrowski opened this issue Jun 21, 2024 · 0 comments

Comments

@MikeDabrowski
Copy link

We are using zod with ts-rest and node express.

I created and endpoint that uses a function which can return 3 different types based on one argument.
The function is getFile and the argument is serveAs: 'stream' | 'url' | 'data'. The way I coded it is function overload statements.

export async function getFile(serveAs: 'data'): Promise<File>;
export async function getFile(serveAs: 'stream'): Promise<{ file: File, fileStream: SdkStream<IncomingMessage> }>;
export async function getFile(serveAs: 'url'): Promise<{ url: string }>;
export async function getFile(serveAs: 'data' | 'file' | 'url' = 'file' ) { ... }

Because we are using sequelize v6, the model is still "in js code" and lacks proper and robust typing. The model is simply the File returned from the topmost overload, and at that point is a JSON object (calling toJSON after getting the response from the db).

The contract for this endpoint is declared as a z.union

z.union([
  // For serveAs: url
  z.object({ url: z.string() }),
  // For serveAs: file
  z.instanceof(Stream),
  // For serveAs: data
  FileDto
])

where FileDto is the schema reflecting the db model z.object({ ... }).

Localy it runs fine. No problems whatsoever. But once I pushed the code for a PR our github action for testing failed complaining.

Error: Jest: Got error running globalSetup - /codebuild ... testSetup.js, reason: [TSError: app/controllers/emi/files.controller.ts:32:14 - error TS2322: Type '({ params, res, query }: { params: { id: string; }; query: { serveAs?: "data" | "file" | "presigned-url" | undefined; expiresIn?: number | undefined; }; headers: { [x: string]: string | string[] | undefined; [x: number]: string | ... 1 more ... | undefined; authorization?: string | undefined; "x-product-name"?: stri...' is not assignable to type 'AppRouteQueryImplementation<{ metadata: { 'x-audience': Audience; tags: string[]; operationId: string; }; method: "GET"; description: "Get file by id"; query: ZodObject<{ serveAs: ZodOptional<ZodEnum<["file", "presigned-url", "data"]>>; expiresIn: ZodOptional<...>; }, "strip", ZodTypeAny, { ...; }, { ...; }>; ... 4 ...'.
  Type 'Promise<{ status: 200; body: Stream; } | { status: 200; body: string; } | { status: 200; body: File; }>' is not assignable to type 'Promise<Prettify<AppRouteResponses<{ metadata: { 'x-audience': Audience; tags: string[]; operationId: string; }; method: "GET"; description: "Get file by id"; query: ZodObject<{ serveAs: ZodOptional<ZodEnum<["file", "presigned-url", "data"]>>; expiresIn: ZodOptional<...>; }, "strip", ZodTypeAny, { ...; }, { ...; }>;...'.
    Type '{ status: 200; body: Stream; } | { status: 200; body: { url: string }; } | { status: 200; body: File; }' is not assignable to type 'Prettify<AppRouteResponses<{ metadata: { 'x-audience': Audience; tags: string[]; operationId: string; }; method: "GET"; description: "Get file by id"; query: ZodObject<{ serveAs: ZodOptional<ZodEnum<["file", "presigned-url", "data"]>>; expiresIn: ZodOptional<...>; }, "strip", ZodTypeAny, { ...; }, { ...; }>; ... 4 m...'.
      Type '{ status: 200; body: File; }' is not assignable to type 'Prettify<AppRouteResponses<{ metadata: { 'x-audience': Audience; tags: string[]; operationId: string; }; method: "GET"; description: "Get file by id"; query: ZodObject<{ serveAs: ZodOptional<ZodEnum<["file", "presigned-url", "data"]>>; expiresIn: ZodOptional<...>; }, "strip", ZodTypeAny, { ...; }, { ...; }>; ... 4 m...'.
        Type '{ status: 200; body: File; }' is not assignable to type '{ status: 200; body: string | Stream | { key: string; location: string; id: string; metadata: { key: string; value: string; }[]; path: string; size: number; createdAt: Date; updatedAt: Date; ... 5 more ...; cdnLing?: string | undefined; }; }'.
          Types of property 'body' are incompatible.
            Type 'File' is not assignable to type '{ url: string } | Stream | { key: string; location: string; id: string; metadata: { key: string; value: string; }[]; path: string; size: number; createdAt: Date; updatedAt: Date; bucket: string; ... 4 more ...; cdnLing?: string | undefined; }'.
              Type 'File' is missing the following properties from type '{ key: string; location: string; id: string; metadata: { key: string; value: string; }[]; path: string; size: number; createdAt: Date; updatedAt: Date; bucket: string; mimeType: string; originalName: string; extension: string; version?: string | undefined; cdnLing?: string | undefined; }': metadata, extension

32 export const findFileById: AppRouteImplementation<
                ~~~~~~~~~~~~]
    at runGlobalHook

If I were to give up, I would just split the endpoint into three separate endpoints for each type. Just not sure if the problem wont be the same in the one that returns FileDto/File

Can anyone tell me what am I doing wrong here?

PS:
I can't fix the typo cdnLing - it does not appear in code anymore but pops up in the log on github...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant