15

So I have a third-party SDK written as an oldschool IIFE based module. In other words it looks something like this:

var ThirdPartySDK = (function() {
  var export = {};

  // Add some methods to export

  return export;
})();

You would then be expected to use it by referencing it on the global scope like this:

<html>
  <body>
    <script src="lib/ThirdPartySDK.js">
    <script>
      ThirdPartySDK.foo();
    <\script>
  <\body>
<\html>

I could still use it this way of course, but is that really the best practice with Angular and TypeScript? Is there some way to set things up with angular/TypeScript/webpack so that I can use a proper import statement? Something like this:

import { ThirdPartySDK } from '../lib/ThirdPartySDK.js';
ThirdPartySDK.foo();

2 Answers 2

6

The best way to have a proper import statement for the actual value of ThirdPartySDK is to refactor the script to a module that exports this value. The following snippet allows you to use the import statement as showed:

export const ThirdPartySDK = {
    foo() { console.log('Doing foo'); }
};

For big libraries refactoring is not always that easy, so I see 2 approaches that do not involve too much refactoring:


1. Export the ThirdPartySDK variable

You could simply make a module out of the IIFE file by exporting the current IThirdPartySDK variable (returned by the IIFE), and then import it as you showed:

export const ThirdPartySDK = (function() {
    var _export = {};

    // Add some methods to export

    return _export;
})();

Note that if you want to have some useful information about the shape of ThirdPartySDK you would have to add a type annotation to the const declaration, and if SomeType (see below) does not yet exist you'll have to write it yourself:

export const ThirdPartySDK: SomeType = (function() {
// ...

At this point Typescript will start to complain about the IIFE expression not being assignable to SomeType; the quick 'solution' to tell typescript to pretend the expression evaluates to a value of type SomeType using the as keyword:

export const ThirdPartySDK: SomeType = (function() {
    // ...
})() as SomeType;

2. Keep the <script> tag and declare the variable

Another option it to keep the script tag, import nothing, and declare the variable and its expected type in typescript:

(But also in this case you might have to provide type definitions yourself)

interface SomeType {
    // SDK type shape goes here...
}

declare const ThirdPartySDK: SomeType;
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! I am a little reticent to modify the source file with an export statement, because I want to be able to just drop in a new version if/when it comes. The declaration might be the only way to go, but I was hoping there was something clever I could do like isolate/export the IIFE's global scope in a separate module or something.
3

You can wrap the third-party SDK in a TypeScript module using a hack with eval.

Let's say that ThirdPartySDK.js looks like this:

var ThirdPartySDK = (function () {
    var exports = {
        foo: function () { console.log("Hello, world!"); }
    };
    return exports;
})();

You would then create a ThirdPartySDK-wrapper.ts module that looks something like this:

import * as fs from 'fs';
const script = fs.readFileSync('../lib/ThirdPartySDK.js').toString();

global.eval(script);

//@ts-ignore
export default ThirdPartySDK;

The @ts-ignore directive is required to keep the TypeScript compiler from complaining about not finding a declaration for the ThirdPartySDK variable (it is declared in the script executed through eval).

You can then import ThirdPartySDK through the wrapper module:

import ThirdPartySDK from './wrapper';
ThirdPartySDK.foo();  // Console output: "Hello, world!" 

Note that this wrapper only works for applications running in Node.js, since it uses fs.fileReadSync to get the contents of the script.

If you're going to use it in a browser, you will need some other way to retrieve the script. You could probably use frameworks such as WebPack to bundle the ThirdPartySDK script as a string value that you can require in the wrapper module.

5 Comments

In the browser unfortunately. I'm a little surprised there is no standard solution for this considering how many IIFE modules used to be kicking around.
@ZacDelventhal: Okay, so which module loader are you using? It shouldn't be too hard to adapt this approach to also work in the browser.
It's an Angular 6/TypeScript front-end using webpack.
@ZacDelventhal: I haven't played around with Angular or Webpack in a long time (it was Angular 2.0 back then), but I could try experimenting a little to find out the details for how to make my approach work there, if that's what it's going to take for you to accept my answer :-) In the meantime, you might want to edit the question to include this requirement and also add the webpack tag.
Haha. I like your answer, but I don't think it is ideal, at least given my tool chain. I am playing around with an angular/webpack solution now, and will post it if I get it working. Took your advice and updated the tags/title/description to reflect the whole tool chain though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.