Skip to content

Commit 17f1593

Browse files
authored
fix inferring the correct fn type (#218)
* 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
1 parent 9bfed09 commit 17f1593

File tree

4 files changed

+142
-62
lines changed

4 files changed

+142
-62
lines changed

types/example-async.test-d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { FastifyPluginAsync } from "fastify";
2+
3+
type FastifyExampleAsync = FastifyPluginAsync<fastifyExampleAsync.FastifyExampleAsyncOptions>;
4+
5+
declare namespace fastifyExampleAsync {
6+
7+
export interface FastifyExampleAsyncOptions {
8+
foo?: 'bar'
9+
}
10+
11+
export interface FastifyExampleAsyncPluginOptions extends FastifyExampleAsyncOptions {
12+
}
13+
export const fastifyExampleAsync: FastifyExampleAsync
14+
export { fastifyExampleAsync as default }
15+
}
16+
17+
declare function fastifyExampleAsync(...params: Parameters<FastifyExampleAsync>): ReturnType<FastifyExampleAsync>
18+
19+
export default fastifyExampleAsync

types/example-callback.test-d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { FastifyPluginCallback } from "fastify";
2+
3+
type FastifyExampleCallback = FastifyPluginCallback<fastifyExampleCallback.FastifyExampleCallbackOptions>;
4+
5+
declare namespace fastifyExampleCallback {
6+
7+
export interface FastifyExampleCallbackOptions {
8+
foo?: 'bar'
9+
}
10+
11+
export interface FastifyExampleCallbackPluginOptions extends FastifyExampleCallbackOptions {
12+
}
13+
export const fastifyExampleCallback: FastifyExampleCallback
14+
export { fastifyExampleCallback as default }
15+
}
16+
17+
declare function fastifyExampleCallback(...params: Parameters<FastifyExampleCallback>): ReturnType<FastifyExampleCallback>
18+
19+
export default fastifyExampleCallback

types/plugin.d.ts

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
RawServerDefault,
99
FastifyTypeProvider,
1010
FastifyTypeProviderDefault,
11+
FastifyBaseLogger,
1112
} from 'fastify'
13+
import { IncomingMessage, Server, ServerResponse } from 'http'
1214

1315
type FastifyPlugin = typeof fastifyPlugin
1416

@@ -45,40 +47,16 @@ declare namespace fastifyPlugin {
4547
* @param fn Fastify plugin function
4648
* @param options Optional plugin options
4749
*/
48-
declare function fastifyPlugin<
49-
Options extends FastifyPluginOptions,
50-
RawServer extends RawServerBase = RawServerDefault,
51-
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
52-
>(
53-
fn: FastifyPluginAsync<Options, RawServer, TypeProvider>,
54-
options?: fastifyPlugin.PluginMetadata
55-
): FastifyPluginAsync<Options, RawServer, TypeProvider>;
56-
57-
declare function fastifyPlugin<
58-
Options extends FastifyPluginOptions,
59-
RawServer extends RawServerBase = RawServerDefault,
60-
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
61-
>(
62-
fn: FastifyPluginAsync<Options, RawServer, TypeProvider>,
63-
options?: string
64-
): FastifyPluginAsync<Options, RawServer, TypeProvider>;
65-
66-
declare function fastifyPlugin<
67-
Options extends FastifyPluginOptions,
68-
RawServer extends RawServerBase = RawServerDefault,
69-
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
70-
>(
71-
fn: FastifyPluginCallback<Options, RawServer, TypeProvider>,
72-
options?: fastifyPlugin.PluginMetadata
73-
): FastifyPluginCallback<Options, RawServer, TypeProvider>;
7450

7551
declare function fastifyPlugin<
76-
Options extends FastifyPluginOptions,
77-
RawServer extends RawServerBase = RawServerDefault,
78-
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
52+
Options extends FastifyPluginOptions = Record<never, never>,
53+
RawServer extends RawServerBase = RawServerDefault,
54+
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
55+
Logger extends FastifyBaseLogger = FastifyBaseLogger,
56+
Fn extends FastifyPluginCallback<Options, RawServer, TypeProvider, Logger> | FastifyPluginAsync<Options, RawServer, TypeProvider, Logger> = FastifyPluginCallback<Options, RawServer, TypeProvider, Logger>
7957
>(
80-
fn: FastifyPluginCallback<Options>,
81-
options?: string
82-
): FastifyPluginCallback<Options>;
58+
fn: Fn extends unknown ? Fn extends (...args: any) => Promise<any> ? FastifyPluginAsync<Options, RawServer, TypeProvider, Logger> : FastifyPluginCallback<Options, RawServer, TypeProvider, Logger> : Fn,
59+
options?: fastifyPlugin.PluginMetadata | string
60+
): Fn;
8361

8462
export = fastifyPlugin

types/plugin.test-d.ts

Lines changed: 94 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import fastifyPlugin from '..';
2-
import fastify, { FastifyPluginCallback, FastifyPluginAsync, FastifyError, FastifyInstance, FastifyPluginOptions } from 'fastify';
3-
import { expectAssignable } from 'tsd'
2+
import fastify, { FastifyPluginCallback, FastifyPluginAsync, FastifyError, FastifyInstance, FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault, FastifyBaseLogger } from 'fastify';
3+
import { expectAssignable, expectError, expectNotType, expectType } from 'tsd'
44
import { Server } from "node:https"
55
import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"
6+
import fastifyExampleCallback from './example-callback.test-d';
7+
import fastifyExampleAsync from './example-async.test-d';
68

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

1517
const pluginCallback: FastifyPluginCallback = (fastify, options, next) => { }
16-
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallback))
18+
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback))
1719

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

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

23-
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallback, '' ))
24-
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallback, {
27+
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback, ''))
28+
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback, {
2529
fastify: '',
2630
name: '',
2731
decorators: {
28-
fastify: [ '', testSymbol ],
29-
reply: [ '', testSymbol ],
30-
request: [ '', testSymbol ]
32+
fastify: ['', testSymbol],
33+
reply: ['', testSymbol],
34+
request: ['', testSymbol]
3135
},
32-
dependencies: [ '' ],
36+
dependencies: [''],
3337
encapsulate: true
3438
}))
3539

3640
const pluginCallbackWithOptions: FastifyPluginCallback<Options> = (fastify, options, next) => {
37-
expectAssignable<string>(options.foo)
41+
expectType<string>(options.foo)
3842
}
3943

40-
expectAssignable<FastifyPluginCallback<Options>>(fastifyPlugin(pluginCallbackWithOptions))
44+
expectType<FastifyPluginCallback<Options>>(fastifyPlugin(pluginCallbackWithOptions))
4145

4246
const pluginCallbackWithServer: FastifyPluginCallback<Options, Server> = (fastify, options, next) => {
43-
expectAssignable<Server>(fastify.server)
47+
expectType<Server>(fastify.server)
4448
}
4549

46-
expectAssignable<FastifyPluginCallback<Options, Server>>(fastifyPlugin(pluginCallbackWithServer))
50+
expectType<FastifyPluginCallback<Options, Server>>(fastifyPlugin(pluginCallbackWithServer))
4751

48-
const pluginCallbackWithTypeProvider: FastifyPluginCallback<Options, Server, TypeBoxTypeProvider> = (fastify, options, next) => {}
52+
const pluginCallbackWithTypeProvider: FastifyPluginCallback<Options, Server, TypeBoxTypeProvider> = (fastify, options, next) => { }
4953

50-
expectAssignable<FastifyPluginCallback<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginCallbackWithTypeProvider))
54+
expectType<FastifyPluginCallback<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginCallbackWithTypeProvider))
5155

5256
// Async
5357

5458
const pluginAsync: FastifyPluginAsync = async (fastify, options) => { }
55-
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsync))
59+
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync))
5660

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

60-
expectAssignable<FastifyPluginAsync>(fastifyPlugin(async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { }))
61-
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsync, '' ))
62-
expectAssignable<FastifyPluginAsync>(fastifyPlugin(pluginAsync, {
64+
expectType<FastifyPluginAsync<FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault>>(fastifyPlugin(async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { }))
65+
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync, ''))
66+
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync, {
6367
fastify: '',
6468
name: '',
6569
decorators: {
66-
fastify: [ '', testSymbol ],
67-
reply: [ '', testSymbol ],
68-
request: [ '', testSymbol ]
70+
fastify: ['', testSymbol],
71+
reply: ['', testSymbol],
72+
request: ['', testSymbol]
6973
},
70-
dependencies: [ '' ],
74+
dependencies: [''],
7175
encapsulate: true
7276
}))
7377

7478
const pluginAsyncWithOptions: FastifyPluginAsync<Options> = async (fastify, options) => {
75-
expectAssignable<string>(options.foo)
79+
expectType<string>(options.foo)
7680
}
7781

78-
expectAssignable<FastifyPluginAsync<Options>>(fastifyPlugin(pluginAsyncWithOptions))
82+
expectType<FastifyPluginAsync<Options>>(fastifyPlugin(pluginAsyncWithOptions))
7983

8084
const pluginAsyncWithServer: FastifyPluginAsync<Options, Server> = async (fastify, options) => {
81-
expectAssignable<Server>(fastify.server)
85+
expectType<Server>(fastify.server)
8286
}
8387

84-
expectAssignable<FastifyPluginAsync<Options, Server>>(fastifyPlugin(pluginAsyncWithServer))
88+
expectType<FastifyPluginAsync<Options, Server>>(fastifyPlugin(pluginAsyncWithServer))
8589

86-
const pluginAsyncWithTypeProvider: FastifyPluginAsync<Options, Server, TypeBoxTypeProvider> = async (fastify, options) => {}
90+
const pluginAsyncWithTypeProvider: FastifyPluginAsync<Options, Server, TypeBoxTypeProvider> = async (fastify, options) => { }
8791

88-
expectAssignable<FastifyPluginAsync<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginAsyncWithTypeProvider))
92+
expectType<FastifyPluginAsync<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginAsyncWithTypeProvider))
8993

9094
// Fastify register
9195

@@ -100,3 +104,63 @@ server.register(fastifyPlugin(pluginAsyncWithTypes))
100104
server.register(fastifyPlugin(pluginAsyncWithOptions))
101105
server.register(fastifyPlugin(pluginAsyncWithServer))
102106
server.register(fastifyPlugin(pluginAsyncWithTypeProvider))
107+
108+
// properly handling callback and async
109+
fastifyPlugin(function (fastify, options, next) {
110+
expectType<FastifyInstance>(fastify)
111+
expectType<Record<never, never>>(options)
112+
expectType<(err?: Error) => void>(next)
113+
})
114+
115+
fastifyPlugin<Options>(function (fastify, options, next) {
116+
expectType<FastifyInstance>(fastify)
117+
expectType<Options>(options)
118+
expectType<(err?: Error) => void>(next)
119+
})
120+
121+
fastifyPlugin<Options>(async function (fastify, options) {
122+
expectType<FastifyInstance>(fastify)
123+
expectType<Options>(options)
124+
})
125+
126+
expectAssignable<FastifyPluginAsync<Options, RawServerDefault, FastifyTypeProviderDefault, FastifyBaseLogger>>(fastifyPlugin(async function (fastify: FastifyInstance, options: Options) { }))
127+
expectNotType<any>(fastifyPlugin(async function (fastify: FastifyInstance, options: Options) { }))
128+
129+
fastifyPlugin(async function (fastify, options: Options) {
130+
expectType<FastifyInstance>(fastify)
131+
expectType<Options>(options)
132+
})
133+
134+
fastifyPlugin(async function (fastify, options) {
135+
expectType<FastifyInstance>(fastify)
136+
expectType<Record<never, never>>(options)
137+
})
138+
139+
expectError(
140+
fastifyPlugin(async function (fastify, options: Options, next) {
141+
expectType<FastifyInstance>(fastify)
142+
expectType<Options>(options)
143+
})
144+
)
145+
expectAssignable<FastifyPluginCallback<Options>>(fastifyPlugin(function (fastify, options, next) { }))
146+
expectNotType<any>(fastifyPlugin(function (fastify, options, next) { }))
147+
148+
fastifyPlugin(function (fastify, options: Options, next) {
149+
expectType<FastifyInstance>(fastify)
150+
expectType<Options>(options)
151+
expectType<(err?: Error) => void>(next)
152+
})
153+
154+
expectError(
155+
fastifyPlugin(function (fastify, options: Options, next) {
156+
expectType<FastifyInstance>(fastify)
157+
expectType<Options>(options)
158+
return Promise.resolve()
159+
})
160+
)
161+
162+
server.register(fastifyExampleCallback, { foo: 'bar' })
163+
expectError(server.register(fastifyExampleCallback, { foo: 'baz' }))
164+
165+
server.register(fastifyExampleAsync, { foo: 'bar' })
166+
expectError(server.register(fastifyExampleAsync, { foo: 'baz' }))

0 commit comments

Comments
 (0)