3

I have a TypeScript file config.ts that will be run with node:

import myDependency = require('my-dependency');    

export = {
    doSomething = () => {
        ...
    }
}

In other TypeScript file, I can import this file with full type safety:

import config = require('./config');
config.doSomething();
config.doSomethingElse(); // compiler error, this method doesn't exist

Now I want to unit test this script. In order to mock out the dependencies that this script require()s I'm using proxyquire, which lets me provide the values that my script will get when it makes calls to require(). Here's what my test might look like:

import proxyquire = require('proxyquire');
const config = proxyquire('./config', {
    'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});

expect(config.doSomething()).to.do.something();

This works fine, except that my config variable is of type any because I'm using proxyquire() in place of require(). TypeScript must give the require() function special treatment to allow it to perform module resolution. Is there a way to tell the TypeScript compiler that proxyquire() should also do module resolution, similar to require()?

I could rewrite config.ts as a class or make it use an interface. Then I would be able to explicitly type the variables in my tests by importing the class/interface definition. But allowing proxyquire() to implicitly type things for me would be far be the easier solution.

Nathan Friend
  • 12,155
  • 10
  • 75
  • 125

1 Answers1

1

There is a workaround - you can get the type of config.ts module by importing actual module and using typeof in a type cast:

import proxyquire = require('proxyquire');

import configType = require('./config');

const config = <typeof configType> proxyquire('./config', {
    'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});

config.doSomething();

// config.noSuchMethod(); // does not compile

This is not ideal because you have to import the same module in your test twice - the real one just to get at the type of it and "proxiquired" one to actually use in your tests, and you have to be careful not to mix up the two. But it's pretty simple compared to the task of implementing another variant of module resolution for typescript. Also, when configType is used in this way - for typing only - its import will not even appear in generated javacsript code.

artem
  • 46,476
  • 8
  • 74
  • 78