'How to use typescript declarations with puppeteer exposeFunction and webpack

I am using puppeteer and webpack in combination for a project. I compile my files into one bundle, and then using the addScriptTag method on puppeteer, adding that file to the browser. In addition, I am using the exposeFunction functionality so I can send/receive data from the browser to nodejs, more specifically, so I can make some database calls. My dilemma is that typescript does not recognize these functions, since they are never actually declared in my bundle, instead on the nodejs side, like so:

  await page.exposeFunction("getPlayerFromDB", async (options) => {
// Some database call
    return DB.players.get(options);
  });

And then in my files I reference it like so:

const player = await getPlayerFromDB(options)

Of course that throws a typescript error of: "Cannot find name 'getPlayerFromDB'"

At first I just @ts-ignore, but as I have more of these exposedFunctions, and because the props for them are kind of complex, it would be nice to benefit from the autocompletion and typechecking of typescript. Therefore I found about the .d.ts files and declaring functions, however I ran into a whole more problems, which I will explain below.

  1. Problem with .d.ts files: Module not found
export declare function getPlayerFromDB(
  options: Partial<Pick<Player, "name" | "auth" | "ip">>
): Promise<DBUser> | null;

and importing like:

import { getPlayerFromDB } from "../utils/puppeteer";

For reference, ../utilts/puppeteer is the file I am making all my exposeFunction function declarations.

I guess webpack cant find the .d.ts file? Even when I go in the compiler options and add the .d.ts extension, or I reference the import ending it with .d, it still cant find the module.

  1. Removed the .d.ts, extension and just sticking with .ts

Now typescript compiles, but my code doesn't work, I receive the error: "puppeteer_1.getPlayerFromDB) is not a function" Im guessing that this is because webpack is converting it to a module, but puppeteer expects just the function name.

So my question is how do I get around this? I can always just @ts-ignore every time I use one of the exposedFunctions OR I can just write the function declaration inside the file every time I use it, but that's not really practical.



Solution 1:[1]

I finally found a way to work around this. Not saying this is the final solution, but this allows me to:

  • Have one file where I declare all my functions, repeating declarations
  • Keep the modular system
  • Allows for intellisense and typescript annotations
  • No ts-ignore

Essentially, I created a module with functions that would act as wrappers around my puppeteer functions. I noticed that webpack doesn't change the function names at all when they are nested in another function. Then I came across the problem that I would have the same function names, since I would have a function like so:

// Puppeteer exposeFunction
declare function getUser(options): DBUser

export default module DB {
export async function getUser(options): DBUser {
return await getUser(options)
}
}

To work around this I renamed all my puppeteer functions to include an underscore, that way I could easily recognize all my native puppeteer functions. Like so:

// Puppeteer exposeFunction
declare function _getUser(options): DBUser

export default module Database {
export async function getUser(options): DBUser {
return await _getUser(options)
}
}

Now anytime I create an exposeFunction my process is

  1. Have it start with an underscore
  2. Declare the function in my Database.ts file
  3. In the module exported in my Database.ts file, export a function with the exposeFunction name minus the underscore
  4. Have that function return my exposeFunction

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Atomixx