Skip to content

Fix validate() return type #58

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

flying-sheep
Copy link

@flying-sheep flying-sheep commented Sep 15, 2021

Checklist

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flying-sheep
Copy link
Author

all done. I also completely split the possible function signatures.

@@ -17,6 +17,7 @@ app.register(fastifyBasicAuth, {
expectType<string>(password)
expectType<FastifyRequest>(req)
expectType<FastifyReply>(reply)
if (Math.random() > 0.5) return new Error()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this throw instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The document is using return and it is actually allowed by this line.

result.then(done, done)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer two asserts group instead:

  • One returning Promise<Error>
  • One returning Promise<void>

@flying-sheep
Copy link
Author

Why did the tests not run?

@mcollina
Copy link
Member

mcollina commented Nov 3, 2021

They did.

There are errors:

  index.test-d.ts:16:4
  ✖  15:44  Parameter username implicitly has an any type.                                                                                               
  ✖  15:54  Parameter password implicitly has an any type.                                                                                               
  ✖  15:64  Parameter req implicitly has an any type.                                                                                                    
  ✖  15:69  Parameter reply implicitly has an any type.                                                                                                  
  ✖  16:4   Parameter type string is not identical to argument type any.                                                                                 
  ✖  17:4   Parameter type string is not identical to argument type any.                                                                                 
  ✖  18:4   Parameter type FastifyRequest<RouteGenericInterface, Server, IncomingMessage> is not identical to argument type any.                         
  ✖  19:4   Parameter type FastifyReply<Server, IncomingMessage, ServerResponse, RouteGenericInterface, unknown> is not identical to argument type any.  

  8 errors

@flying-sheep
Copy link
Author

Ah, I guess it’s this one: microsoft/TypeScript#598

@stale
Copy link

stale bot commented Apr 16, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Apr 16, 2022
@flying-sheep
Copy link
Author

So how do we fix this? I can’t write a test if TypeScript bugs out on me

@stale stale bot removed the stale label Apr 19, 2022
@mcollina
Copy link
Member

cc @fastify/typescript can you help?

@evanshortiss
Copy link
Member

@flying-sheep you need to address the type checker error, e.g change validatePromise (username, password, req, reply) to validatePromise (username: string, password: string, req: FastifyRequest, reply: FastifyReply)

@flying-sheep
Copy link
Author

Hmm, I thought the whole point of the test was to check if the types get inferred properly …

@evanshortiss
Copy link
Member

Wow, sorry, you're right. Looks like an odd TS behaviour.

Not sure what's best here. Perhaps export two definitions?

export type FastifyBasicAuthValidateFnCallback = (username: string, password: string, req: FastifyRequest, reply: FastifyReply, done: (err?: Error) => void) => void
export type FastifyBasicAuthValidateFnPromise = (username: string, password: string, req: FastifyRequest, reply: FastifyReply) => Promise<Error|void>


export interface FastifyBasicAuthOptions {
  validate: FastifyBasicAuthValidateFnPromise|FastifyBasicAuthValidateFnCallback;
  authenticate?: boolean | { realm: string };
  header?: string;
}

This way you can do something like this?

app.register(fastifyBasicAuth, {
  validate: function (username, password, req, reply) {
    return new Promise((resolve, reject) => resolve())
  } as FastifyBasicAuthValidateFnPromise
})

Maybe there's a better solution? I quickly tried conditional types (to return void or a Promise) based on the presence of done, but had some issues getting that to work.

@flying-sheep
Copy link
Author

Welp, maybe not possible then. Too bad.

@evanshortiss
Copy link
Member

Yeah, seems like it's necessary to explicitly define the type for the Promise-based approach. Exporting those FastifyBasicAuthValidateFnPromise and FastifyBasicAuthValidateFnCallback seems like an alright compromise perhaps?

At least it will provide a hint via intellisense like so:

Screenshot 2022-04-21 at 1 19 46 PM

The test case could become:

app.register(fastifyBasicAuth, {
  validate: function validateCallback (username, password, req, reply) {
    expectType<string>(username)
    expectType<string>(password)
    expectType<FastifyRequest>(req)
    expectType<FastifyReply>(reply)
    return new Promise<void>((resolve, reject) => {resolve()})
  } as FastifyBasicAuthValidateFnPromise,
  header: 'x-forwarded-authorization'
})

@flying-sheep
Copy link
Author

flying-sheep commented Apr 22, 2022

Maybe we can get a TypeScript wiz in here to help. It would be great to get it to a state where it’s correct but TypeScript can also infer FastifyBasicAuthValidateFnPromise. As it stands, users have to manually say that, which would be a regression.

I tried this, but it didn’t change anything:

type IsArg5Valid<T extends (...a: any) => any, E> = Parameters<T>['length'] extends 5 ? {} : E;

export interface FastifyBasicAuthOptions {
  validate:
    FastifyBasicAuthValidateFnPromise
    | (FastifyBasicAuthValidateFnCallback & IsArg5Valid<FastifyBasicAuthValidateFnCallback, "You need to specify a “done” callback">);
  ...
}

@fox1t
Copy link
Member

fox1t commented Apr 22, 2022

I looked into this and I am not sure what is going on. The main issue might be on the FastifyPlugin generic side. I'll look deeper into this ASAP. Thanks for the PR.

@evanshortiss
Copy link
Member

evanshortiss commented Apr 22, 2022

This isn't specific to FastifyPlugin AFAICT. Just a quirk of TS. Take a look at this playground example that exhibits the same behaviour. A TypeScript wizard can possibly figure out some clever trick though!

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 this pull request may close these issues.

8 participants