TypeScript: "'Promise' is undefined" error in IE11 even though targeting ES5

TypeScript Version: 2.1.1

Code

tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "lib": [
            "es2016",
            "dom"
        ],
        "module": "commonjs",
        "declaration": false,
        "noImplicitAny": false,
        "removeComments": true,
        "experimentalDecorators": true,
        "noLib": false,
        "jsx": "react",
        "outDir": "dist"
    },
    "exclude": [
        "node_modules",
        "static"
    ]
}

ajax.ts

export class Ajax {
    static async getJson<TArray>(url: string) {
        const request = new XMLHttpRequest();
        return new Promise<TArray[]>(resolve => {
            request.onreadystatechange = () => {
                if (request.readyState != XMLHttpRequest.DONE || request.status != 200) return;
                const data = JSON.parse(request.responseText);
                resolve(data);
            };
            request.open("GET", url, true);
            request.send();
        });
    }
}

index.tsx

import { Ajax } from './utilities/ajax';
import { Holiday } from './models/Holiday';
import { Country } from './models/Country';
import { NationList } from './components/NationList';
import { AppState } from './models/AppState';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { HolidayList } from "./components/HolidayList";

initialize();

async function initialize() {
    const countries = await Ajax.getJson<{ id: string, name: string }>('src/data/countries.json');
    const holidays = await Ajax.getJson<{ id: number, countryId: string, date: Date, day: string, name: string }>('src/data/holidays.json');
    const appState = new AppState(countries, holidays);
    ReactDOM.render(
        <div>
            <NationList appState={appState} />
            <HolidayList appState={appState} />
        </div>,
        document.getElementById("root")
    );
}

index.html

  <head>
    <title>Scheduling Assistant</title>
    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.5/bluebird.min.js"></script>-->
  </head>
  <body>
    <div id="root"></div>
    <script src="/static/bundle.js"></script>
  </body>
</html>

Expected behavior

  • Promise is replaced with callbacks, etc. to be compatible with ES5.
  • Runs without error in IE11.

Actual behavior

  • Fails in IE 11 with error SCRIPT5009: 'Promise' is undefined.
  • Only works in IE 11 if bluebird.min.js script element is uncommented.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 7
  • Comments: 18 (6 by maintainers)

Most upvoted comments

@gcnew, Thanks for the quick response. I had read that note in the docs, but I didn’t really “get it” at the time. Now that I’ve experienced it, it makes sense.

Anyway, it’s a little confusing to me what exactly it means when I put "target": "es5" in compilerOptions. I used to think it meant that typescript would generate code that works in an ES5 browser, period. Now, it seems that there are occasional examples where this isn’t the case, and you basically have to carefully read the docs to find those out (or infer it from browser errors).

An interesting counter-example to Promise not working, BTW, is that Symbol does seem to get polyfilled (at least, I get no error in IE 11 when putting code containing Symbol() in the same .ts file as I put Promise()).

So, I’m not seeing a clear rule of thumb on when the typescript compiler will polyfill something and when it won’t. I think it would be very helpful to developers if all ES6 language features and types were polyfilled when specifying "target": "es5". The way it works now is a violation of the principle of least astonishment IMO.

When jsconfig.json target is ‘es5’ and lib has ‘es6’ following should ‘fix’ all IE11 polyfills:

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6"></script>

Well, the issue has been closed, but I haven’t really found the answer to the question here 😦

The lib option is only for typechecking, it doesn’t affect codegen.

@nsteenbeek If ever meet you in person i’m gonna give you a large hug and buy you a beer, your solution helped me out.

@Maximaximum,

The answer for me has been to visit polyfill.io anytime I get a runtime error in IE11. The error almost always gives a hint as to what feature is missing, and polyfill.io almost always has a polyfill for it.

One idea is when hook is available, a tool may be created alongside tsc to install the polyfills you need for the specific target. 🌷

It is possible to emit perfectly fine es5 (or even es3) code for async/await that will work as long as the environment provides a Promise polyfill.

However, it would be nice to have a way of forbidding the compiler from emitting code that may require certain polyfills; maybe throwing a syntax error about forbidden syntax, similar to how TypeScript complains about for of with non-string/array types in es3 targets.