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

Enforce no boolean or string constructors #9082

Merged
merged 7 commits into from
Mar 1, 2017

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Feb 28, 2017

First commit adds a new ESLint rule that errors when Boolean and String constructors are used. Second commit fixes the codebase to adhere to the new rule.

@sophiebits
Copy link
Collaborator

What's the motivation here? Also we should be wary that String(x) is different from '' + x if x is a Symbol, not sure if that might make a difference.

@acdlite
Copy link
Collaborator Author

acdlite commented Feb 28, 2017

What's the motivation here?

  • Slightly better perf in most engines
  • Consistency
  • The reason Sebastian gave me is that someone could override the global String or Boolean, which isn't all that compelling to me by itself but combined with the other reasons it is.

@acdlite
Copy link
Collaborator Author

acdlite commented Feb 28, 2017

Also we should be wary that String(x) is different from '' + x if x is a Symbol

Good point. I'll add a clarification to the error message.

Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really care. I tend to think that the constructors are a little more readable but I'm not opposed to this if y'all want it.

@sebmarkbage
Copy link
Collaborator

The reason "someone could override" is interesting is because it makes it harder for VMs and static compilers to assume that it is not overridden.

The String symbol thing is something to vary about, but generally it would be a bad thing to convert a String to a symbol for things like props['' + name] = .... The only time that usage is valid is for logging and then we shouldn't concat put but treat them as separate variables anyway. E.g. warning(false, 'Hello %s', symbol);.

@sebmarkbage
Copy link
Collaborator

What about Number?

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't feel strongly about this. Slightly prefer the !!bool or '' + thing syntax anyway.

@acdlite
Copy link
Collaborator Author

acdlite commented Feb 28, 2017

Oops forget about Number. I'll add that, too.

@gaearon
Copy link
Collaborator

gaearon commented Mar 1, 2017

I prefer Boolean() and String() but can stop caring for the reasons above.

@@ -167,7 +167,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
callback === null || typeof callback === 'function',
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
String(callback)
// $FlowFixMe - Intentional cast to string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to tell Flow it's intentional, or do we plan to leave these here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll ask the Flow folks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we casting this to a string anyway?

It would be better to let the warning printer cast it to a string, so that it can choose how to visualize it. E.g. imagine console.error('warning', obj) in devtools prints this nicer with an option to expand etc.

@@ -1034,7 +1034,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
function hasCapturedError(fiber : Fiber) : boolean {
// TODO: capturedErrors should store the boundary instance, to avoid needing
// to check the alternate.
return Boolean(
return !!(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this already a boolean?

Copy link
Collaborator Author

@acdlite acdlite Mar 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it was written that way originally because capturedErrors !== null was just capturedErrors, and when it was null the whole expression would evaluate to null unless it was coerced. I'll change it.

@@ -1043,7 +1043,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
function isFailedBoundary(fiber : Fiber) : boolean {
// TODO: failedBoundaries should store the boundary instance, to avoid
// needing to check the alternate.
return Boolean(
return !!(
Copy link
Collaborator

@gaearon gaearon Mar 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question.

@acdlite acdlite force-pushed the nobooleanorstringconstructors branch from c05b7f7 to b963396 Compare March 1, 2017 02:57
@syranide
Copy link
Contributor

syranide commented Mar 1, 2017

I'm curious, couldn't this be done as a post-process build step instead? (if people prefer the syntax)

@gaearon
Copy link
Collaborator

gaearon commented Mar 1, 2017

Seems easier to enforce than forget and be surprised by output.

@acdlite acdlite merged commit 0585ee8 into facebook:master Mar 1, 2017
@@ -470,7 +470,8 @@ function commitCallbacks(finishedWork : Fiber, queue : UpdateQueue, context : mi
typeof callback === 'function',
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: %s',
String(callback)
// $FlowFixMe - Intentional cast to string
'' + callback
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here what?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nvm I see your comment above

@@ -47,7 +47,8 @@ if (__DEV__) {
'%s(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callerName,
String(callback)
// $FlowFixMe - Intentional cast to string
'' + callback
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

@@ -117,7 +117,8 @@ function coerceRef(current: Fiber | null, element: ReactElement) {

function throwOnInvalidObjectType(returnFiber : Fiber, newChild : Object) {
if (returnFiber.type !== 'textarea') {
const childrenString = String(newChild);
// $FlowFixMe - Intentional cast to string
const childrenString = '' + newChild;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this isn't even used when this is a plain Object.

@@ -19,7 +19,8 @@ function validateCallback(callback: ?Function) {
!callback || typeof callback === 'function',
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: %s',
String(callback)
// $FlowFixMe - Intentional cast to string
'' + callback
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't need to cast to string neither.

@sebmarkbage
Copy link
Collaborator

It seems to me that all these Flow errors should not be using string coercion anyway. Seems simple enough to fix.

@acdlite
Copy link
Collaborator Author

acdlite commented Mar 1, 2017

Ok I'll submit a follow up

@acdlite
Copy link
Collaborator Author

acdlite commented Mar 1, 2017

#9087

@justingrant
Copy link
Contributor

FYI, the '' + value pattern is problematic because + calls valueOf and some types' valueOf method will throw. For example, the new ECMAScript "Temporal" date/time API (currently Stage 3) has this throwing-valueOf behavior which prompted #20594 (fixed by #20617). This behavior exists to prevent use of < and > operators to compare Temporal objects.

To avoid similar problems, I just submitted #22064 to change React's use of '' + value to ''.concat(value) and to revise this lint rule's help text. Unfortunately, it looks like ''.concat(value) doesn't pass Flow (facebook/flow#8728).

This PR encouraged this '' + value pattern, so I wanted to ask for your advice. Should React:

  • Use String(value) ?
  • Use string interpolation? (which will transpile down to concat(), AFAIK)
  • Use ''.concat(value) and change the Flow typing of String.prototype.concat to accept mixed args?
  • Use ''.concat(value) and add type casts to bypass the Flow problem?
  • Keep '' + value as the general pattern but special-case places where value can be an object?
  • Something else?

Feel free to answer over in #22064.

cc @ljharb @bvaughn

justingrant added a commit to justingrant/react that referenced this pull request Aug 16, 2021
`''.concat(value)` violates current Flow definitons that require args to
concat to be strings only, even though the MDN docs and ES spec allow
args to be any type. See facebook/flow#8728.

To avoid depending on a change to Flow, this commit changes
`''.concat(value)` to `String(value)` and removes the lint rule
that prohibits use of String(value). This rule was added in facebook#9082.
The other rules in that PR (for Boolean and Number constructors)
are not changed in this commit, only the rule for String.

If it's decided that using `concat` is better, simply omit this commit
and use the previous one instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants