How to import JS file wrapped in IIFE into TS

Viewed 565

I have a TS file, and I want to import the functionality of a JS library that is wrapped in an IIFE. The library currently exposes its functionality by setting properties on the window object. How can I import this library into TS? I've tried adding exports before each function, but I get the error file is not a module. Should I just bite the bullet and copy the code over manually?

To avoid the XY problem, I'm making a website, and both the front and back end have some common functionality that they need to perform. I'm looking into a serverless back end using TS. If that's an anti-pattern and I should do something else entirely, I'll accept that as an answer too.

2 Answers

I didn't find a particularly elegant way to do this, but I got the functionality I wanted by returning the specific functions I need from the anonymous function and exporting the results of the IIFE. Like so:

JS Library (I added the export default and the return { ... })

export default (function () {
    function foo() { ... }
    function bar() { ... }
    function baz() { ... }
    ...

    // the library makes some functions available to the window
    window['baz'] = baz

    // I manually export the functions I need
    return {
        'foo': foo
        'bar': bar
    }
})()

TS file:

import lib from './library.js'
lib.bar()

I also had "esModuleInterop": true in my tsconfig.json file

I accomplished this by publishing and installing an NPM package that contains a custom definition for a library and then tweaking the tsconfig.json to allow it.

  1. Create an index.d.ts file in the same directory as your IIFE library .js file
  2. In index.d.ts, add the single line: declare var libname = require('libname'); For example:
declare var identikon_cljs = require('identikon_cljs');
  1. Publish it as an npm organization or user scoped package: https://docs.npmjs.com/creating-and-publishing-scoped-public-packages
  2. Install the package in your project (e.g. npm i @mpm/identikon --save)
  3. Add or set the following compilerOptions as true to prevent a TypeScript complilation error stating "Initializers are not allowed in ambient contexts":
{
    "compilerOptions": {
        "allowJs": true,
        "allowSyntheticDefaultImports": true,
        "skipLibCheck": true
    }
}
  1. Import the library in your component like so:
import '@mpm/identikon';
  1. You may then access properties and methods of the library as though it was exported as a module. Here is an example of a directive I made:
import { Directive, ElementRef, Input, } from '@angular/core';
import '@mpm/identikon';

@Directive({
    selector: '[identikon]'
})
export class Identikon {

    @Input() width: number = 64;
    @Input() height: number = 64;
    @Input() set identikon(value: string) {
        if (value) {
            let id = 'identikon-' + value + '-' + new Date().getTime().toString();
            this.el.nativeElement.setAttribute('id', id);
            identikon_cljs.core.make_identikon('#' + id, this.width, this.height, value);
        }
    };

    constructor(private el: ElementRef) { }

}
  1. Then I could bind it with ease in an Ionic 2/3 project like so:
<ion-avatar [identikon]="user.avatar" [width]="32" [height]="32" item-end></ion-avatar>
Related