TypeScript ES dynamic `import()`

10,406

This is a bug in the Typescript Compiler which will be fixed in 2.5.

Exporting a default object with a function that imports a file will not compile the import statement into a require statement in Typescript 2.4.x.

For example, while this:

export const sudo = { run() { return import('./test3'); } }

Will compile to this:

exports.sudo = { run: function () { return Promise.resolve().then(function () { return require('./test3'); }); } };

This:

export default { run() { return import('./test3'); } }

Compiles into this:

exports.default = { run: function () { return import('./test3'); } };

Which is obviously wrong. A temporary solution would be this:

export const sudo = { run() { return import('./test3'); } }

export default sudo;

Which compiles (correctly) into this:

exports.sudo = { run: function () { return Promise.resolve().then(function () { return require('./test3'); }); } };
exports.default = exports.sudo;
Share:
10,406

Related videos on Youtube

Roomy
Author by

Roomy

Updated on June 04, 2022

Comments

  • Roomy
    Roomy almost 2 years

    While using the new TypeScript feature, so called ES Dynamic Imports, I am not able to run the code of my isomorphic app on the server side using ts-node.

    It seems like the error does not occur when using the webpack module loader which transpiles the code in it's own way and running resulting files in a browser.

    The error which I've got:

    case 0: return [4 /*yield*/, import("./component/main")];
                                 ^^^^^^
    SyntaxError: Unexpected token import
    

    Usually TypeScript transpiles the import expression to something like that: Promise.resolve(require("./component/main")), but I can't see it there.

    How to fix that? Does it have something common with ts-node? Or there is a "polyfill" for node.js?

    My tsconfig.json file:

    {
      "compilerOptions": {
        "declaration": false,
        "emitDecoratorMetadata": true,
        "allowJs": false,
        "experimentalDecorators": true,
        "importHelpers": true,
        "inlineSourceMap": false,
        "inlineSources": false,
        "lib": [
          "DOM",
          "ES5",
          "ES6",
          "ES7"
        ],
        "listFiles": false,
        "module": "commonjs",
        "noEmitOnError": true,
        "noImplicitAny": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "preserveConstEnums": false,
        "pretty": false,
        "removeComments": false,
        "strict": true,
        "target": "es5"
      }
    }
    

    the code:

    import * as m from "mithril";
    
    import LayoutComponent from "./component/layout";
    
    const render = (
        layout: m.ComponentTypes<any, any>,
    ) => ({ tag, attrs }: m.Vnode<any, any>) => m(layout, attrs, m(tag as any, attrs));
    
    export default {
        "/:path...": {
            onmatch: async (args, path) => (await import("./component/main")).default,
            render: render(LayoutComponent),
        },
    } as m.RouteDefs;
    
    • Roomy
      Roomy almost 7 years
      it didn't worked, sorry
    • Roomy
      Roomy almost 7 years
      > tsc "--version" Version 2.4.2, > ts-node "--version" ts-node v3.3.0 node v8.2.1 typescript v2.4.2
    • Aluan Haddad
      Aluan Haddad almost 7 years
      "lib": ["dom", "esnext"] would be better. Not saying it's the issue but I advise changing it.
    • Michael Fedora
      Michael Fedora
      I replicated the error, working on solution now...
    • Michael Fedora
      Michael Fedora
      Are you using local (node_modules) tsc/ts-node or global? Are those local/global versions? If you're running linux, does sudo have different version than your local user? etc. That's the only other things I can think of.
    • Aluan Haddad
      Aluan Haddad
      To get this to work I had to pass the --compiler argument explicitly to ts-node. Make it is using the correct version of typescript.
    • Roomy
      Roomy
      @edit I've pasted the code which is used
    • Roomy
      Roomy
      @edit - updated by tsconfig.json
    • Michael Fedora
      Michael Fedora
      tsc --version gives you what?
  • Roomy
    Roomy almost 7 years
    Okay, so in other words I'm forced to use Promise instead of async/await? import(...).then((cmp) => cmp.default) should do the trick, am I correct?
  • Michael Fedora
    Michael Fedora almost 7 years
    No; you are forced to export a non-default object, and then export default that object. Async/await will still work (afaik, I can keep trying)
  • Roomy
    Roomy almost 7 years
    yep, you are right, I can not use that syntax, thanks for explaining that
  • Michael Fedora
    Michael Fedora almost 7 years
    You can still keep the rest of your code the same (using the "default" object, etc), just make sure you are also exporting a sudo-default first, and then exporting said sudo-default as the "default" so that it compiles correctly.
  • Frank Nocke
    Frank Nocke over 6 years
    I am on Typescript 2.5.3 (judged from package.version and yarn list) and the bug is still present.
  • Michael Fedora
    Michael Fedora over 6 years
    Seems to be fixed in 2.6.
  • chharvey
    chharvey about 5 years
    off-topic question, but what’s the difference between Promise.resolve().then(() => require('./test3')) and Promise.resolve(require('./test3'))?
  • Michael Fedora
    Michael Fedora about 5 years
    the first will run the lambda function when the promise is resolved, the second will run the require function before it is resolved, because you are not passing a function there but rather the result of the require() call.