hapijs / hapi

The Simple, Secure Framework Developers Trust

Home Page:https://hapi.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Declaring multiple typescript PluginProperties yields errors at runtime

mattgif opened this issue · comments

Support plan

  • is this issue currently blocking your project? (yes/no): No
  • is this issue affecting a production system? (yes/no): No

Context

  • node version: 16.19.1
  • module version: 21.3.2
  • environment (e.g. node, browser, native): node
  • used with (e.g. hapi application, another framework, standalone, ...): Hapi, Boom
  • any other relevant information:

How can we help?

What is the recommended approach for declaring the typescript interface for PluginProperties in Hapi 21?

Formerly (upgrading from Hapi 17), we could do this by declaring a module. But doing this in v21 with multiple plugins yields the following error at runtime: error TS2339: Property 'X' does not exist on type 'PluginProperties'. (where 'X' is the name of some declared plugin).

In the code example below handler.ts is the module that throws the error:

myPlugin.ts

declare module '@hapi/hapi' {
  interface PluginProperties {
    MyPlugin: {
      foo: Record<string,string>
    }
  }
}

export default {
  name: 'MyPlugin',
  version: '1.0.0',
  async register(server: Server): Promise<void> {
    server.expose('foo', {
      bar: 'Hello world'
    })
  }
}

anotherPlg.ts

import { Server } from '@hapi/hapi'

declare module '@hapi/hapi' {
  interface PluginProperties {
    Other: {
      bar: Record<string,string>
    }
  }
}

export default {
  name: 'Other',
  version: '1.0.0',
  async register(server: Server): Promise<void> {
    server.expose('bar', {
      baz: 'Hello, other world'
    })
  }
}

handler.ts

import { Request, ResponseToolkit } from '@hapi/hapi'

export default function handler (request: Request, h: ResponseToolkit) {
  const {
    server: {
      plugins: {
        MyPlugin: {
         foo
        },
        Other: {
          bar
        }
      }
    }
  } = request
  console.log( foo.bar, bar.baz )
  return h.response().code(200);
}

index.ts

import * as Hapi from "@hapi/hapi";
import MyPlugin from "./myPlugin";
import AnotherPlg from "./anotherPlg";
import handler from "./handler";

const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: "localhost",
  });

  await server.register(MyPlugin);
  await server.register(AnotherPlg);

  server.route({
    method: "GET",
    path: "/",
    handler,
  });

  await server.start();
};

init();

Which version of typescript are you using? I had the same thing for versions < 4.9.

Which version of typescript are you using? I had the same thing for versions < 4.9.

v4.9.5

My workaround for now is an interface declaration like so:

export interface RequestWithPlugins<DataParams = Record<string,any>,QueryParams extends RequestQuery = Record<string,any>>  extends Request {
  server: Server<ServerApplicationState> & {
    plugins: MyPlg & MyOtherPlg & YetAnotherPlg;
    query?: QueryParams;
    data?: DataParams;
  };
}

Where my plugin interfaces look something like:

interface MyPlg extends PluginProperties {
  MyPlugin: {
    foo: {
      bar: 'Hello, world';
    };
  };
}