-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Destructuring #1346
Destructuring #1346
Conversation
Conflicts: src/compiler/binder.ts src/compiler/checker.ts src/compiler/diagnosticInformationMap.generated.ts src/compiler/diagnosticMessages.json
Require RHS of array destructuring to be an actual array type (i.e. assignable to any[]) Tighten test for tuple type (previously just required a "0" property)
Support .d.ts generation for functions with destructuring parameters
function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { | ||
var operator = node.operator; | ||
if (operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteral || node.left.kind === SyntaxKind.ArrayLiteral)) { |
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 is repeated below in the emitter. Extract out a helper 'isDestructuringAssignment' function.
Conflicts: src/compiler/checker.ts src/compiler/diagnosticInformationMap.generated.ts src/compiler/diagnosticMessages.json src/compiler/emitter.ts src/compiler/parser.ts src/compiler/types.ts src/services/navigationBar.ts tests/baselines/reference/assignmentLHSIsValue.errors.txt tests/baselines/reference/objectTypesWithOptionalProperties.errors.txt tests/baselines/reference/parserErrorRecovery_ParameterList2.errors.txt
One thing that's unclear to me is how this works with the 'helicoptering' type operations we use for LS operations. |
You may want to make a new branch for this, with your code copied into it. Right now it's difficult to make out the changes post master-merge (esp in Github). Even looking at the change in a diff tool that can handle the change is tough as your changes are now mixed into everyone else's who checked in in the last month. |
@CyrusNajmabadi Not sure what you mean by the 'helicoptering' question, but certainly the code is written such that you can helicopter into something like Regarding a new branch, not sure that would help any. To get an all up view of how this branch is different from the current master (without everyone's intervening edits) just use the "Files changed" tab at the top of this page (instead of the link to the commit for the merge with master). |
node.name = inGeneratorParameterContext() | ||
? doInYieldContext(parseIdentifier) | ||
: parseIdentifier(); | ||
var savedYieldContext = inYieldContext(); |
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.
Irony, i know: But this seems much more verbose than before.
however, that's not my major issue with this change. My primary concern is that by writing the code in this manner, it puts the onus on the maintainer to be sure that the saved state is restored properly. The prior form ensured that the state would always get saved/restored properly, and no changes could accidently affect that.
If you are concerned that you need to call parseIdentifierOrPattern with a parameter, and you don't want a lambda allocation, then this is a suitable way to write things:
node.name = inGeneratorParameterContext()
? doInYieldContext(parseParameterIdentifierOrPattern)
: parseParameterIdentifierOrPattern();
...
function parseParameterIdentifierOrPattern() {
return parseIdentifierOrPattern(SyntaxKind.Parameter);
}
@ahejlsberg That was indeed what i was referring to when i mentioned 'helicoptering'. Does this support fall out naturally from teh existing code we have that walks upwards/outwards? Or was new code necessary to support the destructuring case? If the latter, could you point out where i should look to find it? Thanks! |
@CyrusNajmabadi The 'helicoptering' ability is in a sense a core requirement because we need the ability to obtain the type of any variable (including one deeply buried in a destructuring declaration) at any time (including both regular compilation and language service use). So, you could say it naturally falls out of the code, but there wasn't really a choice to do it any other way. The interesting functions to look at are Further nuance is added by the way in which a binding pattern can imply a type when no annotation or initializer is available from which to infer, and the effects this has on contextual typing. The newly added comments on |
@ahejlsberg Awesome. Thanks for the explanation. It's definitely helpful to understand how all that 'helicoptering in/out' works. BTW, do you have a better term to describe that process? |
New SyntaxKind.BindingElement Introduced new VariableLikeDeclaration and BindingElement types Cleaned up VariableDeclaration, ParameterDeclaration, PropertyDeclaration types Node kind of binding element is always SyntaxKind.BindingElement Changed CheckVariableDeclaration to CheckVariableLikeDeclaration Reorganized CheckVariableLikeDeclaration
Move downlevel vs. ES6 emit branching into individual emit functions
657f844
to
459dee0
Compare
👍 |
1 similar comment
👍 |
Conflicts: src/compiler/binder.ts src/compiler/checker.ts src/compiler/emitter.ts src/compiler/parser.ts src/services/services.ts tests/baselines/reference/parserCommaInTypeMemberList2.errors.txt
// If no type was specified or inferred for parent, or if the specified or inferred type is any, | ||
// infer from the initializer of the binding element if one is present. Otherwise, go with the | ||
// undefined or any type of the parent. | ||
if (!parentType || parentType === anyType) { |
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.
Shouldn't the type be any if the parent type was any?
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 is if there is not an initializer. If you're suggesting it should be any
regardless of the initializer, then no. Consider:
var a: any;
var [x, y = 1] = a;
Here, a
provides no clue as to the intended type of x
and y
, so we infer from the type of the initializer. This contrasts with:
var a: any;
var [x, y = 1] = [a, a];
Here, the type of a
is the intended type for y
, and the initializer is required to be assignment compatible with that type.
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 do that for contextual typing? An analogous example (courtesy of @CyrusNajmabadi):
var v: (a: any) => void = (a = 1) { a.blah };
Should a
in the body be of type any or number? Applying the reasoning you stated from destructuring, it seems like in this example it should be number, not any.
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 a parameter the order of precedence is this:
- Explicit type annotation.
- Contextual parameter type.
- Initializer type.
For a binding element it is this:
- Deconstructed type (i.e. the type of the corresponding property or array element in the deconstructed value).
- Type of local default value (initializer) if type of deconstructed value is unknown or any.
So, you can't really apply the reasoning of one to the other. They're different kinds of constructs.
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 understand that the way you described is how things are working, but it doesn't seem self explanatory why that is the case. Why are they different constructs?
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 assume you agree with the already existing rules for parameters, so I won't try to elaborate on those.
For a destructuring element, the high order bit is the type of the value being deconstructed. Indeed, the initializer isn't even evaluated when a deconstructed value is available. So, this establishes the intuition that the binding element type should primarily come from the corresponding property or element in the deconstructed type. Then, in cases where the type of the deconstructed value isn't known or is any, it makes sense to take a clue from the initializer. That's the next best source of information.
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 makes a lot of sense, but why is that not the case for parameters? I don't necessarily agree with the rules for parameters. I buy your argument about the destructuring and would like to apply it to parameters
This pull request adds support for ECMAScript 6 destructuring declarations and assignments with down-level ES3 and ES5 code generation. For example:
The above example generates:
Another example:
The above example generates:
Work still remaining: