16

I have node.js application written in TypeScript and I need to switch between two interface implementations based on the config file. Currently I have this code which seems to be working.

"use strict";

import { config } from "../config";

let Something;
if (config.useFakeSomething) {
    Something = require("./FakeSomething").FakeSomething;
} else {
    Something = require("./RealSomething").RealSomething;
}
...
let s = new Something();
s.DoStuff();
...

But I have bad gut feeling about that (mainly because of the mixing require and import for loading modules). Is there some other way how to achieve implementation switching based on config file without importing both modules?

Lukas Pirkl
  • 1,417
  • 3
  • 15
  • 31

5 Answers5

8

If you want to keep the client-code for your Something class clean, you can move the conditional importing to a single file. You can have the following directory structure for your Something module:

/Something
    RealSomething.ts
    FakeSomething.ts
    index.ts

And in your index.ts, you can have the following:

import { config } from '../config';

const Something = config.useFakeSomething ?
    require('./FakeSomething').FakeSomething :
    require('./RealSomething').RealSomething;

export default Something;

And in your client code, you can just import Something:

import Something from './Something/index';
Calvin Belden
  • 3,114
  • 1
  • 19
  • 21
  • But this will load both modules always, won't it? I can't load RealSomething module on dev environment because it is initializing special hardware which is not there. – Lukas Pirkl Mar 11 '16 at 07:26
  • 1
    Yep, you're right. I'll update my answer. I don't think there's anything wrong using require within a TypeScript module to support this dynamic behavior. The issue would arise if you ever switch to a different module loader, but that should be infrequent. – Calvin Belden Mar 11 '16 at 15:56
  • The Fundamental Theorem of Computer Science provides the solution yet again. Love it! – Emperor Eto May 01 '21 at 15:41
6

I can see nothing wrong with your approach. In fact lines like

import { config } from "../config";

When targeting commonjs will be compiled to the following javascript (ES6):

const config = require('../config');

So they are effectively identical and you are not mixing different module loading techniques.

Amid
  • 21,508
  • 5
  • 57
  • 54
3

A modern answer using the import() function:

import { config } from "../config";

let Something;
if (config.useFakeSomething) {
    Something = (await import("./FakeSomething")).FakeSomething;
} else {
    Something = (await import("./RealSomething")).RealSomething;
}
...
let s = new Something();
s.DoStuff();
...

Remember that import() is non blocking, so you need to add await if the code that follows needs the result from the import().

MetaZebre
  • 769
  • 8
  • 10
1

You can do it like that:

let moduleLoader:any;

if( pladform == 1 ) {
    moduleLoader = require('./module1');
} else {
    moduleLoader = require('./module2');
}

and then

if( pladform === 1 ) {
    platformBrowserDynamic().bootstrapModule(moduleLoader.module1, [ languageService ]);
}
else if ( pladform === 2 ) {
    platformBrowserDynamic().bootstrapModule(moduleLoader.module2, [ languageService ]);
}
0

In addition to the correct answers above, in case you need this switching for many files within a single folder, you can use a symbolic-link (not available on Windows) that referenced to the right folder, so your code remains clean.

This approach is good for switching between real code and stubs, for instance

o.z
  • 1,086
  • 14
  • 22