gajus / flow-runtime

A runtime type system for JavaScript with full Flow compatibility.
MIT License
802 stars 57 forks source link

Map class property assertion fails when using babel-plugin-transform-runtime #177

Open jedwards1211 opened 6 years ago

jedwards1211 commented 6 years ago

Issuehunt badges

This is a:

Which concerns:


What is the current behaviour?

Code

// @flow
// @flow-runtime assert, annotate

class Foo {
  bar: Map<string, any> = new Map()

  getValue(name: string) {
    return this.bar.get(name)
  }
}

new Foo().getValue('hello')

.babelrc

{
  "presets": [["env", {"targets": {"node": "current"}}], "flow"],
  "plugins": [
    "transform-decorators-legacy",
    ["flow-runtime", {"annotate": false, "assert": false}],
    "transform-runtime",
    "transform-class-properties",
  ],
}

Erroneous error

/Users/andy/iron-pi-webapp/node_modules/flow-runtime/dist/flow-runtime.js:9331
        throw error;
        ^

RuntimeTypeError: Default value for property Foo.bar must be an instance of Map

Expected: Map

Actual: Map

Transpiled code and explanation

babel-plugin-transform-runtime converts usage of Map to _map2.default, where _map2 is:

var _map = require('babel-runtime/core-js/map');

var _map2 = _interopRequireDefault(_map);

But babel-plugin-flow-runtime decorates the class property with a runtime reference to 'Map', which resolves to something else (depending on the environment).

'use strict';

var _defineProperty = require('babel-runtime/core-js/object/define-property');

var _defineProperty2 = _interopRequireDefault(_defineProperty);

var _map = require('babel-runtime/core-js/map');

var _map2 = _interopRequireDefault(_map);

var _dec, _dec2, _class, _desc, _value, _class2, _descriptor;

var _flowRuntime = require('flow-runtime');

var _flowRuntime2 = _interopRequireDefault(_flowRuntime);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _initDefineProp(target, property, descriptor, context) {
  if (!descriptor) return;
  (0, _defineProperty2.default)(target, property, {
    enumerable: descriptor.enumerable,
    configurable: descriptor.configurable,
    writable: descriptor.writable,
    value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
  });
}

function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
  var desc = {};
  Object['ke' + 'ys'](descriptor).forEach(function (key) {
    desc[key] = descriptor[key];
  });
  desc.enumerable = !!desc.enumerable;
  desc.configurable = !!desc.configurable;

  if ('value' in desc || desc.initializer) {
    desc.writable = true;
  }

  desc = decorators.slice().reverse().reduce(function (desc, decorator) {
    return decorator(target, property, desc) || desc;
  }, desc);

  if (context && desc.initializer !== void 0) {
    desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
    desc.initializer = undefined;
  }

  if (desc.initializer === void 0) {
    Object['define' + 'Property'](target, property, desc);
    desc = null;
  }

  return desc;
}

function _initializerWarningHelper(descriptor, context) {
  throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.');
}

// -runtime assert, annotate

let Foo = (_dec = _flowRuntime2.default.annotate(_flowRuntime2.default.class('Foo', _flowRuntime2.default.property('bar', _flowRuntime2.default.ref('Map', _flowRuntime2.default.string(), _flowRuntime2.default.any())), _flowRuntime2.default.method('getValue', _flowRuntime2.default.param('name', _flowRuntime2.default.string())))), _dec2 = _flowRuntime2.default.decorate(_flowRuntime2.default.ref('Map', _flowRuntime2.default.string(), _flowRuntime2.default.any())), _dec(_class = (_class2 = class Foo {
  constructor() {
    _initDefineProp(this, 'bar', _descriptor, this);
  }

  getValue(name) {
    let _nameType = _flowRuntime2.default.string();

    _flowRuntime2.default.param('name', _nameType).assert(name);

    return this.bar.get(name);
  }
}, (_descriptor = _applyDecoratedDescriptor(_class2.prototype, 'bar', [_dec2], {
  enumerable: true,
  initializer: function () {
    return new _map2.default();
  }
})), _class2)) || _class);

new Foo().getValue('hello');

What is the expected behaviour?


babel-plugin-flow-runtime should use functions to resolve names at runtime (e.g. _flowRuntime2.default.ref(() => Map, ...) instead of _flowRuntime2.default.ref('Map', ...), so that Babel's module transform doesn't break the reference to Map.

Which package versions are you using?

    "babel-core": "^6.26.0",
    "babel-plugin-flow-runtime": "^0.15.0",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.6.1",
    "babel-preset-flow": "^6.23.0",

IssueHunt Summary ### Backers (Total: $50.00) - [issuehunt issuehunt](https://issuehunt.io/u/issuehunt) ($50.00) #### [Become a backer now!](https://issuehunt.io/r/gajus/flow-runtime/issues/177) #### [Or submit a pull request to get the deposits!](https://issuehunt.io/r/gajus/flow-runtime/issues/177) ### Tips - Checkout the [Issuehunt explorer](https://issuehunt.io/r/gajus/flow-runtime/) to discover more funded issues. - Need some help from other developers? [Add your repositories](https://issuehunt.io/r/new) on IssueHunt to raise funds. --- IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
issuehunt-oss[bot] commented 5 years ago

@issuehunt has funded $50.00 to this issue.