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

fix: apply toPropertyKey when defining class members #15182

Merged
merged 2 commits into from Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -6,7 +6,7 @@
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
Object.defineProperty(target, babelHelpers.toPropertyKey(descriptor.key), descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
Expand Down
5 changes: 4 additions & 1 deletion packages/babel-helpers/src/helpers.ts
Expand Up @@ -78,13 +78,14 @@ helpers.classCallCheck = helper("7.0.0-beta.0")`
`;

helpers.createClass = helper("7.0.0-beta.0")`
import toPropertyKey from "toPropertyKey";
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i ++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor);
}
}

Expand Down Expand Up @@ -137,7 +138,9 @@ helpers.defaults = helper("7.0.0-beta.0")`
`;

helpers.defineProperty = helper("7.0.0-beta.0")`
import toPropertyKey from "toPropertyKey";
export default function _defineProperty(obj, key, value) {
key = toPropertyKey(key);
// Shortcircuit the slow defineProperty path when possible.
// We are trying to avoid issues where setters defined on the
// prototype cause side effects under the fast path of simple
Expand Down
@@ -0,0 +1,17 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((class { static [foo] = 0 }).foo).toBe(0);
expect((class { static [foo](){ return 0; } }).foo()).toBe(0);
expect((class { static get [foo](){ return 0; } }).foo).toBe(0);
expect((class { static set [foo](v){ return v; } }).foo = 0).toBe(0);

expect((new class { [foo] = 0 }).foo).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => class { static [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");

expect(() => new class { [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,17 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((class { static [foo] = 0 }).foo).toBe(0);
expect((class { static [foo](){ return 0; } }).foo()).toBe(0);
expect((class { static get [foo](){ return 0; } }).foo).toBe(0);
expect((class { static set [foo](v){ return v; } }).foo = 0).toBe(0);

expect((new class { [foo] = 0 }).foo).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => class { static [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");

expect(() => new class { [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,122 @@
var _class;
var foo = {
[Symbol.toPrimitive]: () => "foo"
};
expect((_class = /*#__PURE__*/babelHelpers.createClass(function _class() {
"use strict";

babelHelpers.classCallCheck(this, _class);
}), babelHelpers.defineProperty(_class, foo, 0), _class).foo).toBe(0);
expect( /*#__PURE__*/function () {
"use strict";

function _class2() {
babelHelpers.classCallCheck(this, _class2);
}
babelHelpers.createClass(_class2, null, [{
key: foo,
value: function () {
return 0;
}
}]);
return _class2;
}().foo()).toBe(0);
expect( /*#__PURE__*/function () {
"use strict";

function _class3() {
babelHelpers.classCallCheck(this, _class3);
}
babelHelpers.createClass(_class3, null, [{
key: foo,
get: function () {
return 0;
}
}]);
return _class3;
}().foo).toBe(0);
expect( /*#__PURE__*/function () {
"use strict";

function _class4() {
babelHelpers.classCallCheck(this, _class4);
}
babelHelpers.createClass(_class4, null, [{
key: foo,
set: function (v) {
return v;
}
}]);
return _class4;
}().foo = 0).toBe(0);
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class6() {
babelHelpers.classCallCheck(this, _class6);
babelHelpers.defineProperty(this, foo, 0);
}
return babelHelpers.createClass(_class6);
}())().foo).toBe(0);
var arrayLike = {
[Symbol.toPrimitive]: () => []
};
expect(() => {
var _class7;
return _class7 = /*#__PURE__*/babelHelpers.createClass(function _class7() {
"use strict";

babelHelpers.classCallCheck(this, _class7);
}), babelHelpers.defineProperty(_class7, arrayLike, 0), _class7;
}).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => /*#__PURE__*/function () {
"use strict";

function _class8() {
babelHelpers.classCallCheck(this, _class8);
}
babelHelpers.createClass(_class8, null, [{
key: arrayLike,
value: function () {
return 0;
}
}]);
return _class8;
}()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => /*#__PURE__*/function () {
"use strict";

function _class9() {
babelHelpers.classCallCheck(this, _class9);
}
babelHelpers.createClass(_class9, null, [{
key: arrayLike,
get: function () {
return 0;
}
}]);
return _class9;
}()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => /*#__PURE__*/function () {
"use strict";

function _class10() {
babelHelpers.classCallCheck(this, _class10);
}
babelHelpers.createClass(_class10, null, [{
key: arrayLike,
set: function (v) {
return v;
}
}]);
return _class10;
}()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class12() {
babelHelpers.classCallCheck(this, _class12);
babelHelpers.defineProperty(this, arrayLike, 0);
}
return babelHelpers.createClass(_class12);
}())()).toThrowError("@@toPrimitive must return a primitive value.");
Expand Up @@ -20,7 +20,9 @@ describe("plugin ordering", () => {
plugins: [proposalClassProperties, proposalClassStaticBlock],
}).code,
).toMatchInlineSnapshot(`
"function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
"function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, \\"string\\"); return typeof key === \\"symbol\\" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== \\"object\\" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || \\"default\\"); if (typeof res !== \\"object\\") return res; throw new TypeError(\\"@@toPrimitive must return a primitive value.\\"); } return (hint === \\"string\\" ? String : Number)(input); }
class Foo {}
Foo.foo = Foo.bar;
_defineProperty(Foo, \\"bar\\", 42);"
Expand Down
@@ -0,0 +1,11 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((new class { [foo](){ return 0; } }).foo()).toBe(0);
expect((new class { get [foo](){ return 0; } }).foo).toBe(0);
expect((new class { set [foo](v){ return v; } }).foo = 0).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => new class { [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,11 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((new class { [foo](){ return 0; } }).foo()).toBe(0);
expect((new class { get [foo](){ return 0; } }).foo).toBe(0);
expect((new class { set [foo](v){ return v; } }).foo = 0).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => new class { [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,90 @@
var foo = {
[Symbol.toPrimitive]: () => "foo"
};
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class() {
babelHelpers.classCallCheck(this, _class);
}
babelHelpers.createClass(_class, [{
key: foo,
value: function value() {
return 0;
}
}]);
return _class;
}())().foo()).toBe(0);
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class2() {
babelHelpers.classCallCheck(this, _class2);
}
babelHelpers.createClass(_class2, [{
key: foo,
get: function get() {
return 0;
}
}]);
return _class2;
}())().foo).toBe(0);
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class3() {
babelHelpers.classCallCheck(this, _class3);
}
babelHelpers.createClass(_class3, [{
key: foo,
set: function set(v) {
return v;
}
}]);
return _class3;
}())().foo = 0).toBe(0);
var arrayLike = {
[Symbol.toPrimitive]: () => []
};
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class4() {
babelHelpers.classCallCheck(this, _class4);
}
babelHelpers.createClass(_class4, [{
key: arrayLike,
value: function value() {
return 0;
}
}]);
return _class4;
}())()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class5() {
babelHelpers.classCallCheck(this, _class5);
}
babelHelpers.createClass(_class5, [{
key: arrayLike,
get: function get() {
return 0;
}
}]);
return _class5;
}())()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class6() {
babelHelpers.classCallCheck(this, _class6);
}
babelHelpers.createClass(_class6, [{
key: arrayLike,
set: function set(v) {
return v;
}
}]);
return _class6;
}())()).toThrowError("@@toPrimitive must return a primitive value.");

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.