How to separately type Fastify decorators in different plugins?

Viewed 125

I'm having trouble typing decorators in two plugins (scopes):

import Fastify, { FastifyInstance } from 'fastify'
const fastify = Fastify()

// scope A
fastify.register((instance) => {
  instance.decorate('utilA', 'AAA')
  instance.get('/', (req, reply) => {
    const data = instance.utilA // Property 'utilA' does not exist on type 'FastifyInstance<...>'
    reply.send(data)
  })
}, { prefix: '/A/' })

// scope B
fastify.register((instance) => {
  instance.decorate('utilB', () => 'BBB')
  instance.get('/', (req, reply) => {
    const data = instance.utilB() // Property 'utilB' does not exist on type 'FastifyInstance<...>'
    reply.send(data)
  })
}, { prefix: '/B/' })

I can define types globally:

declare module 'fastify' {
  interface FastifyInstance {
    utilA: string
    utilB: () => string
  }
}

And it solves the compiler errors. But since utilA and utilB are defined globally compiler allows me to use utilB in the scope A and vice versa.

Is there a way to do the same but independently per scope?

1 Answers

No I do not think this is possible with the declare module '' syntax due to the way that TypeScript works (although someone smarter than me might be able to figure out how).

One thing you can do is create a sub type of the FastifyInstance with your new properties (they will need to be optional or TypeScript might get mad at you depending on your tsconfig settings) and type your plugins fastify instance with that type. For example:

// Scope A
import { FastifyInstance } from 'fastify';

type MyPluginsFastifyInstance = FastifyInstance & { myThing?: MyType; }

export default async (fastify: MyPluginFastifyInstance) => {
  ... // Implementation.
  fastify.myThing // TypeScript is happy :)
}

// Scope B
export default async (fastify) => {
  fastify.myThing // TypeScript is mad at you >:(
}
Related