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 Refactor #1569
TypeScript Refactor #1569
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello! Thank you for contributing!
It appears that you have changed the framework code, but the tests that verify your change are missing. Could you please add them?
light-my-request 3.3.0 is out. |
@mcollina on a previous PR (#1532) you said that we do not want to ship @types/pino from Fastify v2. Considering the default logger in Fastify v2 is dependent on pino, how would you expect me to type this? This is similar to the recently fixed issue in Let me know what you think! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Just skimmed it and adding some thoughts.
First, the logger can be provided by a user that conform to the same interface. Second, no one from the pino team is involved in maintaining that module. Thirdly, the api surface of jsonwebtoken is minimal compared to a logger - and it is also a very stable module which is essentially “done”. You might have noticed that my reviews are more strict in certain modules vs others.. mainly because there is a difference. Something that is ok in one, might not be ok in another. |
To support @mcollina's point, this is a valid logger that can be supplied to Fastify: function log (a, ...args) {
console.info.apply(console, [a, ...args])
}
const logger = {
info: log,
error: log,
warn: log,
debug: log,
fatal: log,
trace: log,
child: () => logger
} |
@mcollina sounds good, I'm thinking of a work around that might work for this @jsumners yep I already have that type of custom logger typed out; its the case of a user using Pino where I'd want them to be able to verify they are passing in valid Pino options to the logger setting on the main |
My idea for the Pino types is to include this annotated |
This PR is amazing work, +1. |
@fastify/core what properties exist on the options object in a standard plugin function (not using const plugin = function (fastify, options, next) { ... }
fastify.register(plugin, opts) // the options object on this line. Is it just whatever we wanna pass to the plugin? Based on Option 1
export type FastifyPlugin<
RawServer extends RawServerBase = RawServerDefault,
RawRequest extends RawRequestBase = RawRequestDefault,
RawReply extends RawReplyBase = RawReplyDefault
> = (
instance: FastifyInstance<RawServer, RawRequest, RawReply>,
opts: { [key as string]: any },
next: (err?: FastifyError) => void
) => void
const plugin: FastifyPlugin = function (fastify, options, next) { ... }
fastify.register(plugin, opts)
// in this instance `opts` could be an object with any content or Option 2
export type FastifyPlugin<
Options,
RawServer extends RawServerBase = RawServerDefault,
RawRequest extends RawRequestBase = RawRequestDefault,
RawReply extends RawReplyBase = RawReplyDefault
> = (
instance: FastifyInstance<RawServer, RawRequest, RawReply>,
opts: Options,
next: (err?: FastifyError) => void
) => void
// in implementation
interface Options {
foo: string,
bar: string
}
const plugin: FastifyPlugin<Options> = function (fastify, options, next) { ... }
const opts: Options = { foo: 'abc', bar: 'xyz' }
fastify.require(plugin, opts)
// so in this example the user can enforce the types for their plugin via an interface and can do so via generics. Furthermore, the `require` method can pass the generic from `plugin` to its own property `opts`. |
They are whatever the plugin author determines their plugin needs for options. |
@delvedor just to clarify is the work i'm doing here considered a major change right? To start, there are breaking changes in the |
For context on commit 4014e18 the new import structure looks like so: import fastify, { FastifyPlugin, FastifyInstance } from 'fastify'
// import fastify from 'fastify'
// const { fastify, FastifyPlugin } = require('fastify')
// const fastify = require('fastify') In these import statements the Does anyone @fastify/core reject this import style for TypeScript users? FWIW all of this and so much more will be accurately documented in the TypeScript page on the Docs site. |
Is that a breaking change? Also, I’m happy to add some duplication if it makes the life of devs easier. I think exposing the “factory” as a property of itself might be hepful as well. Wdyt? |
Are you thinking something like |
I have no idea what any of this means. I could not begin to offer an opinion on if it should be rejected or accepted. |
I'm mainly inquiring about the fact that under TS, users have to import specific types if they want to use them. Fastify has never exported more than just the factory function so I wanted to check that this change was okay and not going against some previous design decision I am unaware of 👍 |
I think more supporting import { fastify, FastifyPlugin, FastifyInstance } from 'fastify' That will also help esm when it comes out. |
That is easily achievable @mcollina I'll commit that change next and work from there. |
import fastify from 'fastify' Should still work. |
9dd4a56 ~ 🙌 now supports both~ This is invalid import { fastify, FastifyInstance, FastifyPlugin} from './fastify' and import fastify, { FastifyInstance, FastifyPlugin} from './fastify' |
@Ethan-Arrowood would that work with actual code? I'm not really convinced. |
I mean, does it also run? I don’t think we export fastify.fastify in commonjs. |
Oh I see now (import/export is very confusing to me). Okay yes you're right the |
Very excited for this PR, already playing around with it locally. If a plugin is decorating the FastifyInstance, the decoration still needs to be declared using type augmentation I guess? I tried a type augmentation like in this example with no luck. Now resorting to use lots of Can you point me in the right direction on how to type plugins that decorate the fastify server? |
New type Is it only available on the |
Yes.
I don't understand this question. It's available in |
I meant the reply object in the handler versus the reply object in the other route methods. But anyways it seems like it should be available on all of them so I'll make the appropriate change in the types and add some additional tests 👍 |
The reply object is the same object everywhere. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-reviewed. After I resolve these comments this PR is good to merge.
Edits from my self review are complete. Big updates:
With that said this PR is finally ready to go -- for real this time. Important Tomorrow I am leaving for a month and a half trip to Europe. I will not have my computer with me and will only be able to review/discuss from my phone. While I'm gone feel free to merge this, let it sit, or even push updates to the branch to finish it. No matter what I'll be back contributing in full force in mid October! |
@delvedor @evanshortiss @SkeLLLa would you like to give this a final pass before we land? |
@mcollina this PR LGTM. |
Landed! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, amazing work!
* utilize conditional to control generic specifics * Constrain and default generics * change declaration name for testing. define overload factory func * simplify to a single function declaration again * completed route method type declarations * split up types * remove fastify2.d.ts file * Add light-my-request and move logger to own file * Add FastifyError type * Utilize DRY RawBases and RawDefault logic * Update logger types * Fix generics in logger * Update route do add shorthand with handler param * Type register and plugin. Modify export structure * Move instance to own file and fix imports * Suppor esm and default import syntax * modify exports and imports for instance and fix generics * add import tests * add addHook methods * Add content-type-parser types * Export all types. Ready for review 🚀 * fix route types (change to interfaces) * Initial implementation of tsd tests. Modify types to interfaces * delete old types * More testing and updates * added server factory and more tests * update jsdocs, update tsd * delete old type test * update npm scripts, remove typescript linting * fix npm script * udpate schema type and doc * try adding explicit azure-pipeline trigger * revert 6e83a45 * fix strict log types * Improve route generics * fix serverFactory test * Add abarre review * Updated content-type-parser tests * update logger types * improve types for schema, context, routes, and register * add context to other route based methods * add import syntax comment * add linting for typescript files * fix export * remove comment * combine Raw(Req|Reply) Base and Default * allow request headers to be mergable * fix hooks * add Symbol to decorate method * fix file paths for windows
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Checklist
npm run test
andnpm run benchmark
I finally feel ready to share my TypeScript refactor ✨ Still WIP and I'll use this PR to track what needs to be down. I'm open to taking other's contributions if they would like work on this with me (this would require making a PR to my personal fork:branch). Ref #1523 for history
TODO:
light-my-request
pending Add TypeScript typings light-my-request#39 for inject typestsd
Some additional context. The fundamental change here is the way I handle the raw HTTP/S/2 server, request, and reply. This is treated as a generic that is passed down through instances of interfaces and types. Additionally, I am using the
type T<G> = G && { }
to create a generic inheritance type for the FastifyRequest and FastifyReply interfaces. The core generic logic is the following:It starts in the function declaration for
fastify
main build method. Using TS, the developer would specify which kind of server they are using (it defaults to http). Then, thisRawServer
type is passed down through to various interfaces such asFastifyInstance
andServerOptions
where it utilizes two TS language features: generic constraints and conditional generic defaults.Generic Constraints (which is actually being used in the first function declaration) is limiting the
RawServer
generic to be of one of the 4 listed types. Furthermore, the= http.Server
is a generic default assignment; this means that the user can define the function without passing in a type to the generic i.e. both of these assignments are valid:Conditional Generic Defaults is a really unique feature that allows us to default the assignment of the other generic variables based on the assignment of the
RawServer
. This enforces http users to use http request and response objects. Similar effects for https and http2 constraints.So all in all, when a user first creates their
fastify
instance, they only have to pass in the server type and then the rest is magically assigned for them. But, at the same time, all of these interfaces and such are exported so if a user really wants to specify a unique FastifyWhatever type they can do that!