Wrap a problematic npm package, while maintaining type information

Cannot use namespace 'ProblematicPackage' as a type.

Well, you can get the typeof that namespace, which seems to be what you want.

To test this, I setup the following:

// problem.js
export function doErrorProneButNecessaryThing(n) {
  return n;
}
export function doErrorProneButNecessaryThing2(s) {
  return s;
}

console.log('did side effect');
// problem.d.ts
export function doErrorProneButNecessaryThing(n: number): number;
export function doErrorProneButNecessaryThing2(s: string): string;

And now you can do:

import type * as ProblemNs from './problem';

type Problem = typeof ProblemNs;

// works
type A = Problem['doErrorProneButNecessaryThing'] // type A = (n: number) => number

Then the wrapProblematicRequire function just takes the name of the function as a generic, pulls the args for it, and pulls the return type.

const wrapProblematicRequire = <TName extends FunctionNamesOf<Problem>>(
  name: TName,
  ...args: Parameters<Problem[TName]>
): ReturnType<Problem[TName]> | undefined => {
  if (!window) return;

  const problem = require('./problem'); // type is any, but types are enforced above

  try {
    return problem[name](...args);
  } catch (err) {
    console.log('error!');
  }
};

Here require('./problem') returns the any type, but the generics keep everything key safe as long as typeof ProblemNs can be trusted.

Now to test that:

console.log('start');
const result: number = wrapProblematicRequire(
  'doErrorProneButNecessaryThing',
  123
);
console.log('end');

Which logs:

start
did side effect
end

Which seems to work!

Codesandbox