September 7th, 2020

Using ts-node with Global Types

Like many TypeScript projects, I’m using mocha to run tests via ts-node. Everything was working great until the project needed to introduce a global variable requiring a global.d.ts type declaration file. Compiling the project worked, but ts-node seemingly couldn’t find the type declaration.

Updated: 12/30/21 for recent versions of TypeScript/ts-node

We can use declare to tell TypeScript that global contains a property with a given type.

declare global
 let myGlobal: number
}

Now we’re able to set global.myGlobal in the application without type errors.

For older versions of TypeScript

Getting right into it with a contrived example, given a global.d.ts declaring a global variable, named myGlobal:

declare const myGlobal: number

namespace NodeJS {
  interface Global {
    myGlobal: number
  }
}

And a file that defines the global via global.myGlobal = 100, now any script that uses myGlobal via ts-node will fail with the following error:

TSError: ⨯ Unable to compile TypeScript:
test/mainTest.ts:3:17 - error TS2552: Cannot find name 'myGlobal'.

This is happening because ts-node isn’t looking for global.d.ts file. After doing some research the first time I ran into this, I found two common suggestions:

  1. Pass the --files flag to ts-node so ts-node will compile all TypeScript files defined by your tsconfigs files key. The large downside here being that each time you use ts-node all of those files will be compiled resulting in slower startup time.
  2. Define typeRoots. Again, this works, but you no longer have automatic type declaration detection.

Because of the downsides each solution came with, I searched for a better approach and thankfully, found one. Well, two.

The first option was to use a triple slash directive to associate the file that declares the global value with the declaration file.

/// <reference types="./global" />
global.myGlobal = 100

Note: Omit the .d.ts file extension.

This effectively says ‘this file depends on these types, when importing me please import the types too’. Running npx tsc completed successfully, as did our ts-node powered mocha tests.

The next solution seemed almost obvious after I found it. The global.d.ts file can be imported.

import "./global.d"
global.myGlobal = 100

Now running tsc and our ts-node script, both complete successfully.

It took a bit of trial, error, and google, but I’m pretty happy with how simple the solution ended up being.