invariant icon indicating copy to clipboard operation
invariant copied to clipboard

Invariant can't be imported in TypeScript under jest vs metro-bundler/packager (in react-native)

Open fbartho opened this issue 7 years ago • 6 comments

Using invariant imported with the following two patterns fails opposite depending on how we run the build (either as unit-tests under jest, or as a react-native view on android).

import * as invariant from "invariant";
import invariant from "invariant";

Swapping the import from one to the other enables/disables using invariant in the opposite target.

fbartho avatar Jan 25 '18 01:01 fbartho

@fbartho do you have babel in your build config? I have no problems with jest, because I have ts transpiler only, when I use it with webpack ts and babel, then I have an error invariant is not a function.

maciejw avatar Feb 05 '18 04:02 maciejw

@maciejw Yes. I have both babel and webpack as I'm using ReactXP to target both web & react-native.

fbartho avatar Feb 05 '18 06:02 fbartho

I have been able to work around this in our project by porting the code to TypeScript. Not sure if anybody would want that.

fbartho avatar Feb 05 '18 06:02 fbartho

ok I have some info regarding subject. https://www.typescriptlang.org/docs/handbook/declaration-files/library-structures.html thats the reason I think

The Impact of ES6 on Module Call Signatures Many popular libraries, such as Express, expose themselves as a callable function when imported. For example, the typical Express usage looks like this:

import exp = require("express");
var app = exp();

In ES6 module loaders, the top-level object (here imported as exp) can only have properties; the top-level module object is never callable. The most common solution here is to define a default export for a callable/constructable object; some module loader shims will automatically detect this situation and replace the top-level object with the default export.

this is how invariant is exported

module.exports = invariant;

so bundler should shim it, this could be configuration issue, or invariant library could add "module" next to "main" in package.json and export invariant function from another file correctly as es6 modules require it, default or named way, to resolve this

as a workaround require can be used, and then reexport using es6 syntax.

or we can PR this if this last solution is OK :)

maciejw avatar Feb 06 '18 18:02 maciejw

Prior to TypeScript 2.7, CommonJS modules that exported a function as the module (i.e. module.exports = function () { … }) could not be imported as an ES module. This meant that it was not possible to import this type of modules ("callable CommonJS module") using ES module import syntax (e.g. import foo from 'foo' or import * as foo from 'foo').

This changed in TypeScript 2.7 with the introduction of --esModuleInterop that enables TypeScript modules to import "callable CommonJS modules" using ES module default import syntax.

This boils down to:

  • If you're using TS 2.7 or newer with --esModuleInterop this module can be imported via import invariant from 'invariant';
  • Otherwise you must use the import invariant = require('invariant'); syntax.

bradleyayers avatar Feb 13 '18 00:02 bradleyayers

I had to use allowSyntheticDefaultImports: true in my config for import invariant from 'invariant' to work (with TS version 2.7.2).

andrezsanchez avatar Mar 31 '18 17:03 andrezsanchez