Skip to content

Commit

Permalink
fix inferring the correct fn type (#218)
Browse files Browse the repository at this point in the history
* simplify and make the issue aware by using expectType instead of expectAssignable

* fix narrowing partially

* fix typings

* fix typings

* fix

* Apply suggestions from code review
  • Loading branch information
Uzlopak authored Jul 18, 2023
1 parent 9bfed09 commit 17f1593
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 62 deletions.
19 changes: 19 additions & 0 deletions types/example-async.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FastifyPluginAsync } from "fastify";

type FastifyExampleAsync = FastifyPluginAsync<fastifyExampleAsync.FastifyExampleAsyncOptions>;

declare namespace fastifyExampleAsync {

export interface FastifyExampleAsyncOptions {
foo?: 'bar'
}

export interface FastifyExampleAsyncPluginOptions extends FastifyExampleAsyncOptions {
}
export const fastifyExampleAsync: FastifyExampleAsync
export { fastifyExampleAsync as default }
}

declare function fastifyExampleAsync(...params: Parameters<FastifyExampleAsync>): ReturnType<FastifyExampleAsync>

export default fastifyExampleAsync
19 changes: 19 additions & 0 deletions types/example-callback.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FastifyPluginCallback } from "fastify";

type FastifyExampleCallback = FastifyPluginCallback<fastifyExampleCallback.FastifyExampleCallbackOptions>;

declare namespace fastifyExampleCallback {

export interface FastifyExampleCallbackOptions {
foo?: 'bar'
}

export interface FastifyExampleCallbackPluginOptions extends FastifyExampleCallbackOptions {
}
export const fastifyExampleCallback: FastifyExampleCallback
export { fastifyExampleCallback as default }
}

declare function fastifyExampleCallback(...params: Parameters<FastifyExampleCallback>): ReturnType<FastifyExampleCallback>

export default fastifyExampleCallback
42 changes: 10 additions & 32 deletions types/plugin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
RawServerDefault,
FastifyTypeProvider,
FastifyTypeProviderDefault,
FastifyBaseLogger,
} from 'fastify'
import { IncomingMessage, Server, ServerResponse } from 'http'

type FastifyPlugin = typeof fastifyPlugin

Expand Down Expand Up @@ -45,40 +47,16 @@ declare namespace fastifyPlugin {
* @param fn Fastify plugin function
* @param options Optional plugin options
*/
declare function fastifyPlugin<
Options extends FastifyPluginOptions,
RawServer extends RawServerBase = RawServerDefault,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
>(
fn: FastifyPluginAsync<Options, RawServer, TypeProvider>,
options?: fastifyPlugin.PluginMetadata
): FastifyPluginAsync<Options, RawServer, TypeProvider>;

declare function fastifyPlugin<
Options extends FastifyPluginOptions,
RawServer extends RawServerBase = RawServerDefault,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
>(
fn: FastifyPluginAsync<Options, RawServer, TypeProvider>,
options?: string
): FastifyPluginAsync<Options, RawServer, TypeProvider>;

declare function fastifyPlugin<
Options extends FastifyPluginOptions,
RawServer extends RawServerBase = RawServerDefault,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
>(
fn: FastifyPluginCallback<Options, RawServer, TypeProvider>,
options?: fastifyPlugin.PluginMetadata
): FastifyPluginCallback<Options, RawServer, TypeProvider>;

declare function fastifyPlugin<
Options extends FastifyPluginOptions,
RawServer extends RawServerBase = RawServerDefault,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
Options extends FastifyPluginOptions = Record<never, never>,
RawServer extends RawServerBase = RawServerDefault,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
Logger extends FastifyBaseLogger = FastifyBaseLogger,
Fn extends FastifyPluginCallback<Options, RawServer, TypeProvider, Logger> | FastifyPluginAsync<Options, RawServer, TypeProvider, Logger> = FastifyPluginCallback<Options, RawServer, TypeProvider, Logger>
>(
fn: FastifyPluginCallback<Options>,
options?: string
): FastifyPluginCallback<Options>;
fn: Fn extends unknown ? Fn extends (...args: any) => Promise<any> ? FastifyPluginAsync<Options, RawServer, TypeProvider, Logger> : FastifyPluginCallback<Options, RawServer, TypeProvider, Logger> : Fn,
options?: fastifyPlugin.PluginMetadata | string
): Fn;

export = fastifyPlugin
124 changes: 94 additions & 30 deletions types/plugin.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import fastifyPlugin from '..';
import fastify, { FastifyPluginCallback, FastifyPluginAsync, FastifyError, FastifyInstance, FastifyPluginOptions } from 'fastify';
import { expectAssignable } from 'tsd'
import fastify, { FastifyPluginCallback, FastifyPluginAsync, FastifyError, FastifyInstance, FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault, FastifyBaseLogger } from 'fastify';
import { expectAssignable, expectError, expectNotType, expectType } from 'tsd'
import { Server } from "node:https"
import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"
import fastifyExampleCallback from './example-callback.test-d';
import fastifyExampleAsync from './example-async.test-d';

interface Options {
foo: string
Expand All @@ -13,79 +15,81 @@ const testSymbol = Symbol('foobar')
// Callback

const pluginCallback: FastifyPluginCallback = (fastify, options, next) => { }
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallback))
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback))

const pluginCallbackWithTypes = (fastify: FastifyInstance, options: FastifyPluginOptions, next: (error?: FastifyError) => void): void => { }
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallbackWithTypes))
expectNotType<any>(fastifyPlugin(pluginCallbackWithTypes))

expectAssignable<FastifyPluginCallback>(fastifyPlugin((fastify: FastifyInstance, options: FastifyPluginOptions, next: (error?: FastifyError) => void): void => { }))
expectNotType<any>(fastifyPlugin((fastify: FastifyInstance, options: FastifyPluginOptions, next: (error?: FastifyError) => void): void => { }))

expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallback, '' ))
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallback, {
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback, ''))
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback, {
fastify: '',
name: '',
decorators: {
fastify: [ '', testSymbol ],
reply: [ '', testSymbol ],
request: [ '', testSymbol ]
fastify: ['', testSymbol],
reply: ['', testSymbol],
request: ['', testSymbol]
},
dependencies: [ '' ],
dependencies: [''],
encapsulate: true
}))

const pluginCallbackWithOptions: FastifyPluginCallback<Options> = (fastify, options, next) => {
expectAssignable<string>(options.foo)
expectType<string>(options.foo)
}

expectAssignable<FastifyPluginCallback<Options>>(fastifyPlugin(pluginCallbackWithOptions))
expectType<FastifyPluginCallback<Options>>(fastifyPlugin(pluginCallbackWithOptions))

const pluginCallbackWithServer: FastifyPluginCallback<Options, Server> = (fastify, options, next) => {
expectAssignable<Server>(fastify.server)
expectType<Server>(fastify.server)
}

expectAssignable<FastifyPluginCallback<Options, Server>>(fastifyPlugin(pluginCallbackWithServer))
expectType<FastifyPluginCallback<Options, Server>>(fastifyPlugin(pluginCallbackWithServer))

const pluginCallbackWithTypeProvider: FastifyPluginCallback<Options, Server, TypeBoxTypeProvider> = (fastify, options, next) => {}
const pluginCallbackWithTypeProvider: FastifyPluginCallback<Options, Server, TypeBoxTypeProvider> = (fastify, options, next) => { }

expectAssignable<FastifyPluginCallback<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginCallbackWithTypeProvider))
expectType<FastifyPluginCallback<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginCallbackWithTypeProvider))

// Async

const pluginAsync: FastifyPluginAsync = async (fastify, options) => { }
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsync))
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync))

const pluginAsyncWithTypes = async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { }
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsyncWithTypes))
expectType<FastifyPluginAsync<FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault>>(fastifyPlugin(pluginAsyncWithTypes))

expectAssignable<FastifyPluginAsync>(fastifyPlugin(async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { }))
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsync, '' ))
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsync, {
expectType<FastifyPluginAsync<FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault>>(fastifyPlugin(async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { }))
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync, ''))
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync, {
fastify: '',
name: '',
decorators: {
fastify: [ '', testSymbol ],
reply: [ '', testSymbol ],
request: [ '', testSymbol ]
fastify: ['', testSymbol],
reply: ['', testSymbol],
request: ['', testSymbol]
},
dependencies: [ '' ],
dependencies: [''],
encapsulate: true
}))

const pluginAsyncWithOptions: FastifyPluginAsync<Options> = async (fastify, options) => {
expectAssignable<string>(options.foo)
expectType<string>(options.foo)
}

expectAssignable<FastifyPluginAsync<Options>>(fastifyPlugin(pluginAsyncWithOptions))
expectType<FastifyPluginAsync<Options>>(fastifyPlugin(pluginAsyncWithOptions))

const pluginAsyncWithServer: FastifyPluginAsync<Options, Server> = async (fastify, options) => {
expectAssignable<Server>(fastify.server)
expectType<Server>(fastify.server)
}

expectAssignable<FastifyPluginAsync<Options, Server>>(fastifyPlugin(pluginAsyncWithServer))
expectType<FastifyPluginAsync<Options, Server>>(fastifyPlugin(pluginAsyncWithServer))

const pluginAsyncWithTypeProvider: FastifyPluginAsync<Options, Server, TypeBoxTypeProvider> = async (fastify, options) => {}
const pluginAsyncWithTypeProvider: FastifyPluginAsync<Options, Server, TypeBoxTypeProvider> = async (fastify, options) => { }

expectAssignable<FastifyPluginAsync<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginAsyncWithTypeProvider))
expectType<FastifyPluginAsync<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginAsyncWithTypeProvider))

// Fastify register

Expand All @@ -100,3 +104,63 @@ server.register(fastifyPlugin(pluginAsyncWithTypes))
server.register(fastifyPlugin(pluginAsyncWithOptions))
server.register(fastifyPlugin(pluginAsyncWithServer))
server.register(fastifyPlugin(pluginAsyncWithTypeProvider))

// properly handling callback and async
fastifyPlugin(function (fastify, options, next) {
expectType<FastifyInstance>(fastify)
expectType<Record<never, never>>(options)
expectType<(err?: Error) => void>(next)
})

fastifyPlugin<Options>(function (fastify, options, next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
expectType<(err?: Error) => void>(next)
})

fastifyPlugin<Options>(async function (fastify, options) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
})

expectAssignable<FastifyPluginAsync<Options, RawServerDefault, FastifyTypeProviderDefault, FastifyBaseLogger>>(fastifyPlugin(async function (fastify: FastifyInstance, options: Options) { }))
expectNotType<any>(fastifyPlugin(async function (fastify: FastifyInstance, options: Options) { }))

fastifyPlugin(async function (fastify, options: Options) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
})

fastifyPlugin(async function (fastify, options) {
expectType<FastifyInstance>(fastify)
expectType<Record<never, never>>(options)
})

expectError(
fastifyPlugin(async function (fastify, options: Options, next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
})
)
expectAssignable<FastifyPluginCallback<Options>>(fastifyPlugin(function (fastify, options, next) { }))
expectNotType<any>(fastifyPlugin(function (fastify, options, next) { }))

fastifyPlugin(function (fastify, options: Options, next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
expectType<(err?: Error) => void>(next)
})

expectError(
fastifyPlugin(function (fastify, options: Options, next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
return Promise.resolve()
})
)

server.register(fastifyExampleCallback, { foo: 'bar' })
expectError(server.register(fastifyExampleCallback, { foo: 'baz' }))

server.register(fastifyExampleAsync, { foo: 'bar' })
expectError(server.register(fastifyExampleAsync, { foo: 'baz' }))

0 comments on commit 17f1593

Please sign in to comment.