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

Does not type check if withTypeProvider is not fluent #55

Closed
mcollina opened this issue Feb 27, 2023 · 6 comments · Fixed by #56
Closed

Does not type check if withTypeProvider is not fluent #55

mcollina opened this issue Feb 27, 2023 · 6 comments · Fixed by #56

Comments

@mcollina
Copy link
Member

Consider the following:

import Fastify from 'fastify';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import { Type } from '@sinclair/typebox';

const server = Fastify();

server.withTypeProvider<TypeBoxTypeProvider>();

server.post('/', {
  schema: {
    body: Type.Object({
      name: Type.String()
    })
  }
}, async (request, reply) => {
  const { name } = request.body;
  return { hello: name };
})

server.listen({ port: 3000 })

This does not work but it errors with:

server.ts:16:11 - error TS2339: Property 'name' does not exist on type 'unknown'.

16   const { name } = request.body;
             ~~~~

However if we inline the route definition and withTypeProvider call it works:

import Fastify from 'fastify';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import { Type } from '@sinclair/typebox';

const server = Fastify();

server.withTypeProvider<TypeBoxTypeProvider>().post('/', {
  schema: {
    body: Type.Object({
      name: Type.String()
    })
  }
}, async (request) => {
  const { name } = request.body;
  return { hello: name };
})

server.listen({ port: 3000 })
@mcollina
Copy link
Member Author

cc @sinclairzx81 @Eomm @RafaelGSS

@SimenB
Copy link
Member

SimenB commented Feb 27, 2023

you need to do

const typeboxServer = server.withTypeProvider<TypeBoxTypeProvider>();

or add the withTypeProvider call to your initial Fastify().

withTypeProvider doesn't "mutate" the server it's called on, it returns a "new instance" (not really, but TS thinks so)

@RafaelGSS
Copy link
Member

RafaelGSS commented Feb 27, 2023

That's correct. We can't mutate the server instance without returning a new instance with the typebox definitions.

A good strategy always calls it in the server creation:

const fastify = Fastify().withTypeProvider<TypeBoxTypeProvider>()

@sinclairzx81
Copy link
Contributor

sinclairzx81 commented Feb 27, 2023

@mcollina Hi!

The TypeProvider setup does require functions to chain in this way. Each call to withTypeProvider() will remap the current instance generics and return the mapped instance to the caller for left-side assignment or additional chaining.

Type Mapping Statements (An Experiment)

Interestingly, it is actually possible to use TypeScript assertion predicates to map generic arguments with line by line statements (so non-fluent - mutable generics). As a proof of concept, the following implements statement assertions to map Type Providers.

TypeScript Link Here (Note: Type inference may take a little while to load)

const server = Fastify()

// -----------------------------------------------------------------------

Fastify.withTypeProvider<TypeBoxTypeProvider>(server)

server.post('/', {
  schema: {
    body: Type.Object({
      name: Type.String()
    })
  }
}, async (request, reply) => {
  const { name } = request.body // ok
  return { hello: name }
})

// -----------------------------------------------------------------------

Fastify.withTypeProvider<JsonSchemaToTsProvider>(server)

server.post('/', {
  schema: {
    body: {
      type: 'object',
      required: ['name'],
      properties: { name: { type: 'string' } }
    } as const
  }
}, async (request, reply) => {
  const { name } = request.body // ok
  return { hello: name }
})

// -----------------------------------------------------------------------

Fastify.withTypeProvider<ZodTypeProvider>(server)

server.post('/', {
  schema: {
    body: z.object({
      name: z.string()
    })
  }
}, async (request, reply) => {
  const { name } = request.body // ok
  return { hello: name }
})

// -----------------------------------------------------------------------

Fastify.withTypeProvider<YupTypeProvider>(server)

server.post('/', {
  schema: {
    body: yup.object({
      name: yup.string().required()
    })
  }
}, async (request, reply) => {
  const { name } = request.body  // ok
  return { hello: name }
})

@mcollina
Copy link
Member Author

Thanks! I will send a PR to include some of this in the README.

@thelinuxlich
Copy link

I'm still getting untyped responses
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.

5 participants