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

Improve Type Declarations #111

Merged
merged 3 commits into from
Aug 1, 2019
Merged

Conversation

maxmellen
Copy link
Contributor

@maxmellen maxmellen commented May 23, 2019

This PR aims to improve the type declarations to more accurately reflect the documentation in the README as well as allow compilation in strict mode.

It also includes an updated package-lock.json file as a result of me simply running npm install. The only differences in that file seem to be values used by newer versions of NPM.

@coveralls
Copy link

coveralls commented May 23, 2019

Pull Request Test Coverage Report for Build 241

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 99.683%

Totals Coverage Status
Change from base Build 237: 0.0%
Covered Lines: 584
Relevant Lines: 585

💛 - Coveralls

@maxmellen maxmellen changed the title Make type declarations pass strict mode Improve Type Declarations May 23, 2019
@maxmellen
Copy link
Contributor Author

Edit: This PR used to only make lambda-api compatible with TypeScript's strict mode, but after noticing the existing type declarations weren't so accurate, I also included some improvements in this PR and updated the PR's description.

Copy link

@stawecki stawecki left a comment

Choose a reason for hiding this comment

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

This works great! Not sure why the old req/res were optional.

Copy link

@jsepia jsepia left a comment

Choose a reason for hiding this comment

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

I switched to this fork to get proper TypeScript support for route parameters. Works for me. ✅

@maxmellen
Copy link
Contributor Author

@jeremydaly Any chance of seeing this merged?

use (...errorHandlingMiddleware: ErrorHandlingMiddleware[]);
use(path: string, ...middleware: Middleware[]): void;
use(paths: string[], ...middleware: Middleware[]): void;
use(...middleware: Middleware[]): void;
Copy link

Choose a reason for hiding this comment

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

I think you can improve this even further by allowing mixing normal middleware with error-handling middleware, cause if I see correctly the library allows it. Something like this, but prettier of course ;P :

type AnyMiddleware = Middleware | ErrorHandlingMiddleware

use(path: string, ...middleware: Middleware[]): void;
use(paths: string[], ...middleware: Middleware[]): void;
use(...middleware: AnyMiddleware[]): void;

@jalooc
Copy link

jalooc commented Jul 30, 2019

This PR is essential to use this library with TS, it fixes incorrectly typed basic functionalities. Let's merge it!

@@ -36,10 +36,10 @@ export declare interface App {
[namespace: string]: HandlerFunction;
}

export declare type Middleware = (req: Request, res: Response, next: Middleware) => void;
export declare type Middleware = (req: Request, res: Response, next: () => void) => void;
export declare type ErrorHandlingMiddleware = (error: Error, req: Request, res: Response, next: ErrorHandlingMiddleware) => void;
Copy link

Choose a reason for hiding this comment

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

Suggested change
export declare type ErrorHandlingMiddleware = (error: Error, req: Request, res: Response, next: ErrorHandlingMiddleware) => void;
export declare type ErrorHandlingMiddleware = (error: Error, req: Request, res: Response, next: () => void) => void;

Copy link

@jalooc jalooc left a comment

Choose a reason for hiding this comment

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

Sorry for atomic CR comments, I'm finding things as I develop.

export declare type ErrorHandlingMiddleware = (error: Error, req: Request, res: Response, next: ErrorHandlingMiddleware) => void;
export declare type ErrorCallback = (error?: Error) => void;
export declare type HandlerFunction = (req?: Request, res?: Response, next?: NextFunction) => void | {} | Promise<{}>;
export declare type HandlerFunction = (req: Request, res: Response, next?: NextFunction) => void | any | Promise<any>;
export declare type LoggerFunction = (message: string) => void;
Copy link

Choose a reason for hiding this comment

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

https://github.com/jeremydaly/lambda-api#adding-additional-detail

Suggested change
export declare type LoggerFunction = (message: string) => void;
export declare type LoggerFunction = (message: string, additionalDetail?: any) => void;

@jeremydaly
Copy link
Owner

@jalooc, let me know when you think this is ready.

@jalooc
Copy link

jalooc commented Jul 31, 2019

I'm not TS-savvy enough to say if it'd be possible, but it would be very nice to have req typed in a smarter way, for use cases like this:

api.use(async req => {
  req.connection = await createConnection()
  next()
})

// ...

api.finally(req => {
  return req.connection.close() // Can I have the req.connection type automatically inferred?
})

Does anybody know if it's possible? If not automatically inferred, then maybe a generic type for api could be used? 🤔

@maxmellen
Copy link
Contributor Author

@jalooc You'd want to have the req argument to be typed like T extends Response and then you would write your custom type extending Response adding the fields you need. You would then cast inside of the handler:

interface UserReq extends Response {
  userToken: string
}

api.use(req => {
  const userReq = req as UserReq // TypeScript accepts this cast without any warning, even in the strictest settings
  // do something with userReq.userToken...
})

@jalooc @jeremydaly, I'm happy to apply the suggestions, but can we get this merged in a state not so far from how this PR currently stands? It would still be an incremental improvement in the right direction.

Or of course feel free to take over this PR.

@jalooc
Copy link

jalooc commented Aug 1, 2019

@maxmellen , I haven't found any more problems so far, so if you're willing to accept my change suggestions that'd be great (they just follow the lambda-api working, no subjective changes). It's a green light from my side. Nice job!

@jeremydaly jeremydaly merged commit 4f3ee6e into jeremydaly:master Aug 1, 2019
@jalooc
Copy link

jalooc commented Aug 6, 2019

So what we're doing with the discrepancies I listed? Another PR?

@jeremydaly
Copy link
Owner

Yes, please create a new PR.

@jalooc
Copy link

jalooc commented Aug 6, 2019

Just as a reference for others reading this thread and having a similar question as I did:

I'm not TS-savvy enough to say if it'd be possible, but it would be very nice to have req typed in a smarter way, for use cases like this:

api.use(async req => {
  req.connection = await createConnection()
  next()
})

// ...

api.finally(req => {
  return req.connection.close() // Can I have the req.connection type automatically inferred?
})

Does anybody know if it's possible? If not automatically inferred, then maybe a generic type for api could be used? 🤔

@maxmellen The way you suggested is okay in the short run, but after a while of playing with TS, I found a lot of shortcomings. First of all, you'd quickly sink in the depth of overridden interfaces. What's worse, you'd also have to always remember to manually cast X as Y to get the right type and that usually means a lot of places in a standard lambda. Besides, using such overridden type would prove problematic when interconneting it with other functionalities from the 3rd party library that don't understand it, so some as unknown-type hacks would be necessary.

I found an imho better and the proper way to do it: Declaration merging or module augmentation more specifically.

Following the example from my question, this would solve it:

declare module 'lambda-api' {
  interface Request {
    connection: SomeType,
  }
}

and from now on you have a typable req.connection everywhere.

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.

6 participants