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

Deserializing empty attributes when the property type definition is Number results in "0" #147

Closed
klebba opened this issue Aug 1, 2023 · 4 comments · Fixed by #148
Closed

Comments

@klebba
Copy link
Collaborator

klebba commented Aug 1, 2023

Is it a bug?

Consider:

<x-foo baz></x-foo>

Where baz is defined as a Number type property; the value of baz will be coerced to Number 0. This is because, in Javascript, Number('') === 0.

@klebba
Copy link
Collaborator Author

klebba commented Aug 2, 2023

Perhaps:

else if (property.type === Number && value === '') {
  // Handle the case where Number('') === 0
  return Number.NaN;
}

https://github.com/Netflix/x-element/blob/main/x-element.js#L843

One reason not to do this is that it's not actually the normal behavior of JavaScript, because Number('') does become the Number 0 — HOWEVER an empty attribute value needing to be deserialized is a special case (in other words the empty string originates from the DOM API element.getAttribute('baz') === '' — so it seems we have the need to make a decision in x-element (there is no obvious standard to follow)

@klebba
Copy link
Collaborator Author

klebba commented Aug 2, 2023

Funny:

Number('                 ')

This is zero. Wat.

Update: we could do:

'         '.trim() === ''

@theengineear
Copy link
Collaborator

Thanks for the discussion on this one yesterday, @klebba. I think '' >> NaN makes sense for Number. However, it’s interesting to note that the same would not work if BigInt was supported as a serializable type. As far as I understand it, there is no concept of NaN or Infinity when dealing with BigInt. Consider the following cases:

// Coercion of empty string seems roughly equivalent.
Number('') // 0
BigInt('') // 0n

// Where Numbers will quietly convert to NaN, BigInt will just throw.
Number('foobar') // NaN
BigInt('foobar') // Uncaught SyntaxError: Cannot convert foobar to a BigInt

// Where Numbers accommodate Infinity, BigInt will just throw.
Number('Infinity') // Infinity
BigInt('Infinity') // Uncaught SyntaxError: Cannot convert Infinity to a BigInt

// Where Number arithmetic can result in Infinity...
zeroNumber = Number('0')
oneNumber = Number('1')
oneNumber / zeroNumber // Infinity

// ... BigInt arithmetic will just throw.
zeroBigInt = BigInt('0')
oneBigInt = BigInt('1')
oneBigInt / zeroBigInt // Uncaught RangeError: Division by zero

Ultimately, I think if / when we would decide to allow BigInt to be deserialized. We would want to throw an error. I.e., <my-element big-int=""> and <my-element big-int> would throw a halting error (similar to how setting an incorrectly typed value on a property throws a halting error).

theengineear added a commit that referenced this issue Aug 2, 2023
It would always be counter-intuitive to declare `<my-element number>` in
HTML markup and introspect the element to find `element.number === 0`.

In other words, while it’s true that in JS `Number('') === 0`, this is
not sensible behavior when deserializing values in markup into typed
objects in JS.

This is mostly due to the concept of so-called “boolean attributes”. As
a custom element author, you don’t want to coerce a boolean attribute to
a `Number`. In otherwords, when dealing with numbers, the markup
`<my-element number>` should _not_ equal `<my-element number="0">`.

While this change makes a strong opinion, part of the goal of
`x-element` is to reduce obvious boiler plate. Since it seems incredibly
unlikely that any author would actually _desire_ the `Number('') >> 0`
behavior, taking a stance here seems more helpful than hurtful.

Additionally, we will always, conceptually, have a _default_
deserializer. In the future, if needed, we could allow authors to
declare their own deserializers / serializers within property
declarations.

Closes #147.
@klebba
Copy link
Collaborator Author

klebba commented Aug 17, 2023

Agree about BigInt — I'm not as familiar with this type, but prefer to YAGNI its handling for now

theengineear added a commit that referenced this issue Aug 18, 2023
It would always be counter-intuitive to declare `<my-element number>` in
HTML markup and introspect the element to find `element.number === 0`.

In other words, while it’s true that in JS `Number('') === 0`, this is
not sensible behavior when deserializing values in markup into typed
objects in JS.

This is mostly due to the concept of so-called “boolean attributes”. As
a custom element author, you don’t want to coerce a boolean attribute to
a `Number`. In otherwords, when dealing with numbers, the markup
`<my-element number>` should _not_ equal `<my-element number="0">`.

While this change makes a strong opinion, part of the goal of
`x-element` is to reduce obvious boiler plate. Since it seems incredibly
unlikely that any author would actually _desire_ the `Number('') >> 0`
behavior, taking a stance here seems more helpful than hurtful.

Additionally, we will always, conceptually, have a _default_
deserializer. In the future, if needed, we could allow authors to
declare their own deserializers / serializers within property
declarations.

Closes #147.
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

Successfully merging a pull request may close this issue.

2 participants