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

request: minify Boolean(foo) to (!!foo) #2962

Closed
clshortfuse opened this issue Mar 2, 2023 · 3 comments
Closed

request: minify Boolean(foo) to (!!foo) #2962

clshortfuse opened this issue Mar 2, 2023 · 3 comments

Comments

@clshortfuse
Copy link

clshortfuse commented Mar 2, 2023

Hi!

I would like to able to write Boolean(foo) in my code which is clearer, but have esbuild minify it down to !!foo which is faster. That's because Boolean(value) is actually an alias for (new Boolean(foo)).valueOf().

I've researched it a bit and seems it's an safe conversion. You'd probably have to wrap it in parentheses to ensure things like Boolean(value).constructor yields (!!value).constructor, though I wasn't be surprised if there's another optimization step that handles that. Also, new Boolean(value) would need to be guarded against.

Some points of research:

@clydin
Copy link

clydin commented Mar 2, 2023

Non-new Boolean(x) and !!x do use the same underlying ToBoolean abstract operation. However, the same is not true of String(x) and `${x}`. The later will throw for symbol values whereas the former will not.

That's because Boolean(value) is actually an alias for (new Boolean(foo)).valueOf().

While these two will usually return the same result, the non-new Boolean behavior is a subset of the later rather than an alias. Based on the spec, non-new Boolean performs the ToBoolean conversion and then returns immediately with the value.
This can also be demonstrated by the following:

Boolean.prototype.valueOf = function () {
  return false;
};

const x = 1;
console.log(!!x); // true
console.log(Boolean(x)); // true
console.log(new Boolean(x).valueOf()); // false

While these type of optimizations can be useful, they enter into one of the more complex areas of the ECMAScript spec where the transforms can result in subtle but potentially critical differences in runtime behavior.

@clshortfuse
Copy link
Author

clshortfuse commented Mar 2, 2023

Thanks for taking a look! The notes on String coercion do appear on the MDN (I didn't research String as heavily).

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion

Similarly, Number will cause issues with BigInt where it didn't before. That presents the possibility of these optimizations become broken in the future. Though I would consider (!!x) to be safe, and in the unlikely situation that it breaks (a new JS primitive), for it be reverted. esbuild is currently doing (!0) for true and (!1) for false, so it's not new for esbuild to do these optimizations:

https://hyrious.me/esbuild-repl/?version=0.17.10&mode=transform&input=if+%28a+%3D%3D%3D+true%29+return+0%3B%0Aif+%28a+%3D%3D%3D+false%29+return+1%3B%0Aif+%28a+%3D%3D%3D+Boolean%28null%29%29+return+2%3B%0Aif+%28a+%3D%3D%3D+String%28null%29%29+return+3%3B%0Aif+%28a+%3D%3D%3D+Number%28null%29%29+return+4%3B&options=--minify

@evanw evanw closed this as completed in 2176b35 Mar 3, 2023
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

2 participants