Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jQuery with ES6-style import and System.js #6315

Closed
nycdotnet opened this issue Oct 16, 2015 · 13 comments
Closed

jQuery with ES6-style import and System.js #6315

nycdotnet opened this issue Oct 16, 2015 · 13 comments

Comments

@nycdotnet
Copy link
Collaborator

System.js is fairly strict to the spec regarding how it imports modules. Unless a default import is performed, ES6-style imports loaded via System.js will always be an inert object with properties. ("has a", not "is a").

For example, if one uses the below syntax with jQuery, $ will have the jQuery static methods such as .isArray(), but the jQuery function itself will not work when loaded by System.js at runtime.

import * as $ from 'jquery';
$.isArray([]); //works
$('#myDiv'); // $ is not a function at runtime (TypeScript is happy)

If one uses a default ES6-style import with jQuery, $ will work as expected when loaded by System.js.

import $ from 'jquery';
$.isArray([]); //works
$('#myDiv'); //works at runtime, but...

However - this syntax is not supported by the current TypeScript jQuery definition which uses the old-style export = syntax. error TS1192: Module '"jquery"' has no default export.

The current jQuery type definition uses this syntax to support external modules.

declare module "jquery" {
  export = $;
}

Changing the export to an ES6-style export syntax allows the default import to work (and jQuery supports this at runtime with System.js).

declare module "jquery" {
  export {$ as default};
}

However, this breaks the old-style import = require('jquery'); syntax.

import $ = require('jquery');
$.isArray([]);
$('#myDiv');
//error TS2339: Property 'isArray' does not exist on type 'typeof "jquery"'.
//error TS2349: Cannot invoke an expression whose type lacks a call signature.

Is there any way to support both the legacy require() syntax and ES6 via default in a single definition?

//cc: @RyanCavanaugh @johnnyreilly

@johnnyreilly
Copy link
Member

I don't have the answer but I want to know it! @jbrantly may also be able to help given he's written on related issues: http://www.jbrantly.com/es6-modules-with-typescript-and-webpack/

@jbrantly
Copy link
Contributor

AFAIK you can't have it both ways right now. I've asked for some guidance here but haven't really come across any great solutions.

@vvakame
Copy link
Member

vvakame commented Oct 16, 2015

export = $; and export default $; is not satisfied both in same time.
Does jquery allow require("jquery").default(".foo") in no-System.js context?

@nycdotnet
Copy link
Collaborator Author

@vvakame - that is a good point. This code does not work with jQuery at runtime:

$.default.isArray([]);

System.js (or jQuery) must be doing something special to guess at a value for default (or I don't understand the spec enough).

If using Require.js and AMD target, this TypeScript code works with the current DefinitelyTyped jQuery definition at compile time and runtime:

import * as $ from 'jquery';
$.isArray([]);
$('#myDiv');

@jbrantly I think microsoft/TypeScript#4337 is the right place to discuss this further. Thanks.

@nycdotnet
Copy link
Collaborator Author

It turns out that System.js makes a guess at a default export when importing AMD, CommonJS, or "global" modules. See here:

https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#inter-format-dependencies

When loading CommonJS, AMD or Global modules from within ES6, the full module is available at the default export which can be loaded with the default import syntax.

I am going to propose that the TypeScript team allows ES6-style default import with export = syntax and system module format. This doesn't fix microsoft/TypeScript#4337, but it allows SystemJS users to move forward.

@jbrantly
Copy link
Contributor

I think they might be resistant because it depends on runtime behavior. By default TS emits JS that doesn't do those checks on import like System.js or Babel does. At least that was Anders' opinion before. See microsoft/TypeScript#2719

That said, I'd be for it because it's a PITA the way things are now.

@nycdotnet
Copy link
Collaborator Author

2719 was very informative. Thanks. I figure it doesn't hurt to send this proposal to see if they'll rehydrate it. I could be wrong. (I just opened microsoft/TypeScript#5285)

As of right now, this is a true statement: jQuery doesn't work with SystemJS and TypeScript when using ES6 module syntax. Someone really should move to fix that.

@squarewave24
Copy link

squarewave24 commented Aug 5, 2016

i can't even do import * as $ from 'jquery'. I am getting [ts] cannot find module 'jquery'

in my package.json: "jquery": "3.1.0",

I am guessing that error is there because imported package does not 'export' ? did you have to manually change jquery.js after install?

@johnnyreilly
Copy link
Member

You just need:

      "allowSyntheticDefaultImports": true,

in your tsconfig.json

@squarewave24
Copy link

squarewave24 commented Aug 5, 2016

@johnnyreilly that did not work.

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "allowSyntheticDefaultImports": true
  }
}

actually, it did fix it, it's just visual studio still complaining

@arlowhite
Copy link

I know this issue is for SystemJS, but in case this helps, here's what I've found...

  • TypeScript 2.0.3
  • @types/jquery 2.0.32

With angular-cli, which uses webpack, I was having trouble with this.
Part of the issue may be that I have jQuery configured as a global in angular-cli.json "scripts"

"../node_modules/jquery/dist/jquery.js",

I think this just loads jQuery in a script tag as a UMD global? I needed to do this because I'm using a few jQuery plugins that I couldn't import using TypeScript.

Originally, I tried putting import * as $ from 'jquery' in every TypeScript file where I used jQuery. However, I was getting this error:

WEBPACK_IMPORTED_MODULE_17_jquery is not a function

I found that instead I should only have a single import for my entire project, which for an Angular2 project I put in app.module.ts

Also, I found that you can do
import * as $ from 'jquery';
OR simply
import 'jquery';

My theory is that this works because app.module.ts imports jQuery without actually using it. So the $ global becomes available in the TypeScript project for type-checking, but because it's not used in the file where it is imported, webpack doesn't try to load it on top of the global jQuery that has already been loaded.

@johnnyreilly
Copy link
Member

johnnyreilly commented Oct 4, 2016

@squarewave24 this is my tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "lib": ["dom", "es2015", "es2016"],
    "jsx": "preserve",
    "module": "es2015",
    "moduleResolution": "node",
    "noEmitOnError": false,
    "noImplicitAny": true,
    "preserveConstEnums": true,
    "removeComments": false,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es2015"
  }
}

Taken from this project: https://github.com/johnnyreilly/proverb-signalr-react

Works for me - hope it does for you

@pk-developer
Copy link

pk-developer commented Jan 20, 2019

Probably it will help you
TypeStrong/atom-typescript#237 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants