Skip to content

Commit

Permalink
Re-add typed decorators (#4111)
Browse files Browse the repository at this point in the history
* Re-add typed decorators

* Use typescript eslint func-call-spacing
  • Loading branch information
wyozi committed Jul 9, 2022
1 parent e698ab7 commit 20263a1
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 14 deletions.
40 changes: 35 additions & 5 deletions test/types/instance.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,29 +284,59 @@ expectType<string>(server.printRoutes({ includeMeta: ['key1', Symbol('key2')] })

expectType<string>(server.printRoutes())

server.decorate<(x: string) => void>('test', function (x: string): void {
server.decorate('nonexistent', () => {})
server.decorateRequest('nonexistent', () => {})
server.decorateReply('nonexistent', () => {})

declare module '../../fastify' {
interface FastifyInstance {
functionWithTypeDefinition: (foo: string, bar: number) => Promise<boolean>
}
interface FastifyRequest {
numberWithTypeDefinition: number
}
interface FastifyReply {
stringWithTypeDefinition: 'foo' | 'bar'
}
}
expectError(server.decorate('functionWithTypeDefinition', (foo: any, bar: any) => {})) // error because invalid return type
expectError(server.decorate('functionWithTypeDefinition', (foo: any, bar: any) => true)) // error because doesn't return a promise
expectError(server.decorate('functionWithTypeDefinition', async (foo: any, bar: any, qwe: any) => true)) // error because too many args
expectAssignable<FastifyInstance>(server.decorate('functionWithTypeDefinition', async (foo, bar) => {
expectType<string>(foo)
expectType<number>(bar)
return true
}))

expectError(server.decorateRequest('numberWithTypeDefinition', 'not a number')) // error because invalid type
expectAssignable<FastifyInstance>(server.decorateRequest('numberWithTypeDefinition', 10))

expectError(server.decorateReply('stringWithTypeDefinition', 'not in enum')) // error because invalid type
expectAssignable<FastifyInstance>(server.decorateReply('stringWithTypeDefinition', 'foo'))

server.decorate<'test', (x: string) => void>('test', function (x: string): void {
expectType<FastifyInstance>(this)
})
server.decorate('test', function (x: string): void {
expectType<FastifyInstance>(this)
})

server.decorateRequest<(x: string, y: number) => void>('test', function (x: string, y: number): void {
server.decorateRequest<'test', (x: string, y: number) => void>('test', function (x: string, y: number): void {
expectType<FastifyRequest>(this)
})
server.decorateRequest('test', function (x: string, y: number): void {
expectType<FastifyRequest>(this)
})

server.decorateReply<(x: string) => void>('test', function (x: string): void {
server.decorateReply<'test', (x: string) => void>('test', function (x: string): void {
expectType<FastifyReply>(this)
})
server.decorateReply('test', function (x: string): void {
expectType<FastifyReply>(this)
})

expectError(server.decorate<string>('test', true))
expectError(server.decorate<(myNumber: number) => number>('test', function (myNumber: number): string {
expectError(server.decorate<'test', string>('test', true))
expectError(server.decorate<'test', (myNumber: number) => number>('test', function (myNumber: number): string {
return ''
}))

Expand Down
2 changes: 2 additions & 0 deletions types/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"rules": {
"no-console": "off",
"@typescript-eslint/indent": ["error", 2],
"func-call-spacing": "off",
"@typescript-eslint/func-call-spacing": ["error"],
"semi": ["error", "never"],
"import/export": "off" // this errors on multiple exports (overload interfaces)
},
Expand Down
42 changes: 33 additions & 9 deletions types/instance.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,45 @@ export interface FastifyInstance<
close(closeListener: () => void): undefined;

// should be able to define something useful with the decorator getter/setter pattern using Generics to enforce the users function returns what they expect it to
decorate<T>(property: string | symbol,
decorate<K extends keyof FastifyInstance>(
property: K,
value: FastifyInstance[K] extends (...args: any[]) => any
? (this: FastifyInstance, ...args: Parameters<FastifyInstance[K]>) => ReturnType<FastifyInstance[K]>
: FastifyInstance[K],
dependencies?: string[]
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
decorate<K extends string | symbol, T>(
property: NotInInterface<K, FastifyInstance>,
value: T extends (...args: any[]) => any
? (this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>, ...args: Parameters<T>) => ReturnType<T>
: T,
dependencies?: string[]
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;

decorateRequest<T>(property: string | symbol,
decorateRequest<K extends keyof FastifyRequest>(
property: K,
value: FastifyRequest[K] extends (...args: any[]) => any
? (this: FastifyRequest, ...args: Parameters<FastifyRequest[K]>) => ReturnType<FastifyRequest[K]>
: FastifyRequest[K],
dependencies?: string[]
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
decorateRequest<K extends string | symbol, T>(
property: NotInInterface<K, FastifyRequest>,
value: T extends (...args: any[]) => any
? (this: FastifyRequest, ...args: Parameters<T>) => ReturnType<T>
: T,
dependencies?: string[]
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;

decorateReply<T>(property: string | symbol,
decorateReply<K extends keyof FastifyReply>(
property: K,
value: FastifyReply[K] extends (...args: any[]) => any
? (this: FastifyReply, ...args: Parameters<FastifyReply[K]>) => ReturnType<FastifyReply[K]>
: FastifyReply[K],
dependencies?: string[]
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
decorateReply<K extends string | symbol, T>(
property: NotInInterface<K, FastifyReply>,
value: T extends (...args: any[]) => any
? (this: FastifyReply, ...args: Parameters<T>) => ReturnType<T>
: T,
Expand Down Expand Up @@ -137,7 +161,7 @@ export interface FastifyInstance<
* @since This option is available only in Node.js v15.6.0 and greater
*/
signal?: AbortSignal;
}, callback: (err: Error|null, address: string) => void): void;
}, callback: (err: Error | null, address: string) => void): void;
listen(opts?: {
/**
* Default to `0` (picks the first available open port).
Expand Down Expand Up @@ -189,17 +213,17 @@ export interface FastifyInstance<
* @deprecated Variadic listen method is deprecated. Please use `.listen(optionsObject, callback)` instead. The variadic signature will be removed in `fastify@5`
* @see https://github.com/fastify/fastify/pull/3712
*/
listen(port: number | string, address: string, backlog: number, callback: (err: Error|null, address: string) => void): void;
listen(port: number | string, address: string, backlog: number, callback: (err: Error | null, address: string) => void): void;
/**
* @deprecated Variadic listen method is deprecated. Please use `.listen(optionsObject, callback)` instead. The variadic signature will be removed in `fastify@5`
* @see https://github.com/fastify/fastify/pull/3712
*/
listen(port: number | string, address: string, callback: (err: Error|null, address: string) => void): void;
listen(port: number | string, address: string, callback: (err: Error | null, address: string) => void): void;
/**
* @deprecated Variadic listen method is deprecated. Please use `.listen(optionsObject, callback)` instead. The variadic signature will be removed in `fastify@5`
* @see https://github.com/fastify/fastify/pull/3712
*/
listen(port: number | string, callback: (err: Error|null, address: string) => void): void;
listen(port: number | string, callback: (err: Error | null, address: string) => void): void;
/**
* @deprecated Variadic listen method is deprecated. Please use `.listen(optionsObject)` instead. The variadic signature will be removed in `fastify@5`
* @see https://github.com/fastify/fastify/pull/3712
Expand Down Expand Up @@ -526,11 +550,11 @@ export interface FastifyInstance<
/**
* Set the 404 handler
*/
setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema> (
setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema>(
handler: (request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider>, reply: FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfigDefault, SchemaCompiler, TypeProvider>) => void | Promise<RouteGeneric['Reply'] | void>
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;

setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface, ContextConfig extends ContextConfigDefault = ContextConfigDefault, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema> (
setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface, ContextConfig extends ContextConfigDefault = ContextConfigDefault, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema>(
opts: {
preValidation?: preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
preHandler?: preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
Expand Down

0 comments on commit 20263a1

Please sign in to comment.