-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Object spread rest fixes #12248
Object spread rest fixes #12248
Conversation
1. Rename finds identifiers in spread assignment expressions. 2. Spreads with computed properties of type number or any no longer crash the compiler. 3. Rest emit uses indexOf === -1 instead of !indexOf to filter properties. 4. Rest emit correctly refers to computed properties' generated temps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor comments, but generally this looks fine.
@@ -11193,7 +11193,10 @@ namespace ts { | |||
let hasComputedStringProperty = false; | |||
let hasComputedNumberProperty = false; | |||
|
|||
for (const memberDecl of node.properties) { | |||
let i = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it can probably be part of the following for
declaration rather than on its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, left over from an earlier single-variable attempt.
@@ -45,7 +45,7 @@ var __assign = (this && this.__assign) || Object.assign || function(t) { | |||
const restHelper = ` | |||
var __rest = (this && this.__rest) || function (s, e) { | |||
var t = {}; | |||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && !e.indexOf(p)) | |||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) === -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't >= 0
be a few less chars?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even less chars would be !~e.indexOf(p)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is for not found. But I guess i could use < 0
.
@timsuchanek Typescript's emit tries to balance readability, speed and brevity. Brevity is the least important of the three, though, and I think !~e.indexOf(p)
is less readable than e.indexOf(p) < 0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to < 0
.
propertyNames.push(str); | ||
if (isComputedPropertyName(getPropertyName(element))) { | ||
// get the temp name and put that in there instead, like `_tmp + ""` | ||
const stringifiedTemp = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the location for the literal? Also, why not just use createLiteral("", location)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird, last time I looked I thought createLiteral
said that it returns NumericLiteral
. You're right, that's much better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what location is used for -- source maps, right? I'll leave it out of the createLiteral("")
call for now and leave it in elsewhere.
propertyNames.push(createBinary(computedTempVariables.shift(), SyntaxKind.PlusToken, stringifiedTemp)); | ||
} | ||
else { | ||
const str = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For StringLiteral
and Identifier
you can just call createLiteral(element)
, which creates a StringLiteral that reads its literal text from the original identifier. That way you can preserve unicode escape sequences. We could probably add support for NumericLiteral
to createLiteral
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that's how I got confused. Tide for emacs doesn't display overloads and I got shown the number overload of createLiteral
with no indication that there are others.
@@ -548,8 +569,13 @@ namespace ts { | |||
bindingElements = []; | |||
} | |||
// Rewrite element to a declaration with an initializer that fetches property | |||
// TODO: Probably save the result of createDestructuringPropertyAccess if propName is a computed property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this TODO something you still have yet to do for this PR or is it for future work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's already done. I delete the TODO.
emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); | ||
const propAccess = createDestructuringPropertyAccess(value, propName); | ||
if (isComputedPropertyName(propName)) { | ||
(computedTempVariables = computedTempVariables || []).push((propAccess as ElementAccessExpression).argumentExpression); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See append
in core.ts
:
computedTempVariables = append(computedTempVariables, (propAccess as ElementAccessExpression).argumentExpression);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the reminder. I keep forgetting about append
.
@@ -1,42 +1,42 @@ | |||
=== tests/cases/conformance/types/rest/objectRest.ts === | |||
let o = { a: 1, b: 'no' } | |||
>o : Symbol(o, Decl(objectRest.ts, 0, 3)) | |||
var o = { a: 1, b: 'no' } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these suddenly var
and not let
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the source declaration (line 2) so I could reuse o
in a destructuring assignment below.
stringifiedTemp.pos = location.pos; | ||
stringifiedTemp.end = location.end; | ||
stringifiedTemp.text = ""; | ||
propertyNames.push(createBinary(computedTempVariables.shift(), SyntaxKind.PlusToken, stringifiedTemp)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will crash at runtime if the typeof
of the key is "symbol"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you suggest toString
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would crash with "number"
/ "undefined"
/ null
instead :(
A workaround could be to use String()
instead, as that will work with anything (and hopefully nobody actually has a string key that looks like "Symbol()"
), but it would prevent easily improving __rest
into being able to preserve (and filter) Symbol
...
The most correct option would be typeof x == "symbol" ? x : String(x)
.
Update baselines with smaller emit as part of that.
Previously, symbols were not handled.
It used the index of the symbol property as the name, not the symbol itself.
Fixes #12211
Fixes #12200
Fixes #12227
Fixes #12228
New:
Fixes #12252