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

TypeScript reports errors when union type passed as Body to RouteGeneric since Fastify 4 #4063

Closed
2 tasks done
svsool opened this issue Jun 21, 2022 · 3 comments · Fixed by #4076
Closed
2 tasks done

Comments

@svsool
Copy link

svsool commented Jun 21, 2022

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.1.0

Plugin version

No response

Node.js version

18.x

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

12.3

Description

Hi, I came across the following problem after updating to 4.x.

Body property of request gets resolved to unknown instead of { filters?: { ids: string[] } } | null. Use-case: we have some routes that optionally accept body using ajv's anyOf keyword. It used to work in Fastify 3.

Fastify 4:
image

vs.

Fastify 3:
image

Steps to Reproduce

This snippet can be used to reproduce the problem:

import { fastify } from 'fastify';

const app = fastify();

app.route<{
    Body: { filters?: { ids: string[] } } | null;
}>({
    method: 'DELETE',
    url: '/entities',
    schema: {}, // omitted for clarity
    async handler(request, reply) {
        const filters = request.body?.filters;
    },
});

Expected Behavior

body type gets resolved to { filters?: { ids: string[] } } | null instead of unknown.

@svsool svsool changed the title TypeScript reports errors with union type passed as Body to RouteGeneric since Fastify 4 TypeScript reports errors when union type passed as Body to RouteGeneric since Fastify 4 Jun 21, 2022
@A5rocks
Copy link
Contributor

A5rocks commented Jun 21, 2022

I ran into this and investigated a bit (didn't realize someone else had made an issue). Here's what I found:

Firstly, body is governed by ResolveFastifyRequestType<TypeProvider, SchemaCompiler, RouteGeneric>["body"]. In my case, TypeProvider is FastifyTypeProviderDefault, SchemaCompiler is FastifySchema, RouteGeneric is { Body: z.infer<typeof body>, [snip] } where body is a zod.union([..., ..., ...]).

This then gets moved into ResolveRequestBody.

type ResolveRequestBody<TypeProvider extends FastifyTypeProvider, SchemaCompiler extends FastifySchema, RouteGeneric extends RouteGenericInterface> =
UndefinedToUnknown<keyof RouteGeneric['Body'] extends never ? CallTypeProvider<TypeProvider, SchemaCompiler['body']> : RouteGeneric['Body']>

See, keyof <union> can be never. This means it goes to CallTypeProvider<TypeProvider, SchemaCompiler['body']> which returns unknown (as we have not extended FastifyTypeProviderDefault).

What is there to do? Well, checking StackOverflow, I find that something like type KeysOf<T> = T extends T ? keyof T : never; can be used for unions. The only problem is that I'm not quite sure why this conditional exists in the first place for ResolveRequestBody.

tl;dr

Why is ResolveRequestBody conditional on keyof <body type>?

If that is intentional, then type KeysOf<T> = T extends T ? keyof T : never; would work; keyof <body type> -> KeysOf<<body type>>.

@mcollina
Copy link
Member

Thanks for reporting! Would you like to work on a PR with a fix? I'm not sure if your proposed solution is ok, however it mostly depends if it breaks any other tests.

@A5rocks
Copy link
Contributor

A5rocks commented Jun 23, 2022

I believe I fix this in #4076

image

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

Successfully merging a pull request may close this issue.

3 participants