Import polyfill using dynamic imports in TypeScript

TypeScript 2.4 has introduced Dynamic imports. In a previous, we have seen how to use static imports in TypeScript. Static import allow to use a module. You import all the modules needed at the beginning of the TypeScript file and use them later:

import { ZipCodeValidator } from "./ZipCodeValidator";
const myValidator = new ZipCodeValidator();

Dynamic imports on the other hand are not declared at the beggining of the file, but right before their usage. This is very useful to load a module only when you really need it. For instance, if a feature is not commonly use, you can load the code of that feature only when the user starts using it. This way, when the page loads, the user don't need to download all the code, so it reduces the time to load the page.

if (condition) {
    let zipCodeValidator = await import("./ZipCodeValidator");
    let myValidator = new zipCodeValidator();
}

What is a polyfill?

From wikipedia:

In web development, a polyfill is code that implements a feature on web browsers that do not support the feature. Most often, it refers to a JavaScript library that implements an HTML5 web standard, either an established standard (supported by some browsers) on older browsers, or a proposed standard (not supported by any browsers) on existing browsers. Formally, "a polyfill is a shim for a browser API".

Polyfills allow web developers to use an API regardless of whether it is supported by a browser or not, and usually with minimal overhead. Typically they first check if a browser supports an API, and use it if available, otherwise using their own implementation.

For instance, String.includes is not available in Internet Explorer 11. This is a very convenient function, so I want to be able to use it. The idea is to run the polyfill before the rest of the code, so you can use the function. You can find a polyfill on MDN.

// Test if String.includes is available
if (!String.prototype.includes) {

    // Add the find method to the prototype
    String.prototype.includes = function(search, start) {
        'use strict';
        if (typeof start !== 'number') {
            start = 0;
        }

        if (start + search.length > this.length) {
            return false;
        } else {
            return this.indexOf(search,start) !== -1;
        }
    };
}

Load polyfills dynamically

The previous polyfill is not very long. However, if you need to include ten polyfills or a much longer one, you'll increase the size of the JS, and your web site will be slower. Plus, String.includes is well supported by the current browsers. You can check that information on caniuse:

So, you need this polyfill for just a few users. You don't want to impact the vast majority of your audiance. So, why not loading this polyfill dynamically. This is where dynamic import is useful. Indeed, you can create the polyfill as a module and import it only when needed.

Here's the structure I use:

src
├── polyfills
│   ├── array.from.js
│   ├── array.prototype.find.js
│   ├── string.prototype.includes.js
│   └── ...
└── main.ts

Polyfills are often paste from the internet, so you can use JavaScript or TypeScript depending on the source.

Then, you can import them dynamically if needed in the main file:

function importPolyfills() {
    let promises = [];
    if (!String.prototype.includes) {
        promises.push(import("./polyfills/string.prototype.includes"));
    }

    if (!Array.from) {
        promises.push(import("./polyfills/array.from"));
    }

    if (!Array.prototype.find) {
        promises.push(import("./polyfills/array.prototype.find"));
    }

    // ...

    return Promise.all(promises);
}

importPolyfills().then(() => {
    console.log("meziantou".includes("an"));
});

This way, only the users who use a browser that don't support the feature you want will actually download the polyfill. For them, the loading time will increase a little. But, for the vast majorty of the users, it will reduce the loading time. And when a browser releases the functionality, the polyfill won't be downloaded anymore. So, you can measure how many people download the polyfill file and check if it is actually needed.