TypeScript - conditional module import/export

From TypeScript v2.4 you can use dynamic import to achieve conditional importing

An async example:

async function importModule(moduleName: string):Promise<any>{
    console.log("importing ", moduleName);
    const importedModule = await import(moduleName);
    console.log("\timported ...");
    return importedModule;
}

let moduleName:string = "module-a";
let importedModule = await importModule(moduleName);
console.log("importedModule", importedModule);

import { something } from '../here';
import { anything } from '../there';

export const conditionalExport =
  process.env.NODE_ENV === 'production' ? something : anything;

Inspiration from Andrew answer.


I agree that the fact that they can only have toplevel scope is suboptimal at best. Besides the issue you stated, it also means slower initial load times of software. For example within nodejs I now sometimes load a module in a function if that function is seldom used. So my application starts up quicker since it doesn't load that module yet.

And of course you could use require or AMD directly, but than you will miss some of the typing benefits.

I think however that the real problem lies in the fact that harmony/es6 defined modules to be toplevel and TS seems to be following that proposal. So not sure how much TS team can do without diverging from the standards.


I have a slightly clunky but very effective solution for this, particularly if you're using conditional import/export for unit testing.

Have an export that is always emitted, but make the contents vary based on a runtime value. E.g.:

// outputModule.ts
export const priv = (process.env.BUILD_MODE === 'test')
  ? { hydrateRecords, fillBlanks, extractHeaders }
  : null

Then in the consuming file, import the export, check that the imported value exists, and if it does, assign all the values you'd otherwise import stand-alone to a set of variables:

// importingModule.spec.ts
import { priv } from './outputModule';

const { hydrateRecords, fillBlanks, extractHeaders } = priv as any;
// these will exist if environment var BUILD_MODE==='test'

Limitations:

  1. You sadly have to set the import to 'any' to make the compiler happy.
  2. You need to check for whether or not specific imports are defined (but that comes with the territory).
  3. The importing file will expect the values to be defined. You will thus have to ensure importing files actually need the modules (which is fine if you're e.g. dealing with files only run during testing), or you'll have to define alternative values for cases where they don't actually get exported.

Still, it worked really well for me for my purposes, hopefully it works for you too. It's particularly useful for unit testing private methods.

Tags:

Typescript