Skip to content

Commit 1b5dc0d

Browse files
committed
Merge pull request microsoft#5121 from Microsoft/shorthandPropsInDestructuring
parse/check/emit shorthand property assignment in destructuring
2 parents 5736521 + a556209 commit 1b5dc0d

13 files changed

Lines changed: 1125 additions & 38 deletions

src/compiler/checker.ts

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7313,15 +7313,15 @@ namespace ts {
73137313
}
73147314

73157315
function checkObjectLiteral(node: ObjectLiteralExpression, contextualMapper?: TypeMapper): Type {
7316+
let inDestructuringPattern = isAssignmentTarget(node);
73167317
// Grammar checking
7317-
checkGrammarObjectLiteralExpression(node);
7318+
checkGrammarObjectLiteralExpression(node, inDestructuringPattern);
73187319

73197320
let propertiesTable: SymbolTable = {};
73207321
let propertiesArray: Symbol[] = [];
73217322
let contextualType = getContextualType(node);
73227323
let contextualTypeHasPattern = contextualType && contextualType.pattern &&
73237324
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
7324-
let inDestructuringPattern = isAssignmentTarget(node);
73257325
let typeFlags: TypeFlags = 0;
73267326

73277327
for (let memberDecl of node.properties) {
@@ -7345,7 +7345,10 @@ namespace ts {
73457345
if (inDestructuringPattern) {
73467346
// If object literal is an assignment pattern and if the assignment pattern specifies a default value
73477347
// for the property, make the property optional.
7348-
if (memberDecl.kind === SyntaxKind.PropertyAssignment && hasDefaultValue((<PropertyAssignment>memberDecl).initializer)) {
7348+
const isOptional =
7349+
(memberDecl.kind === SyntaxKind.PropertyAssignment && hasDefaultValue((<PropertyAssignment>memberDecl).initializer)) ||
7350+
(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment && (<ShorthandPropertyAssignment>memberDecl).objectAssignmentInitializer);
7351+
if (isOptional) {
73497352
prop.flags |= SymbolFlags.Optional;
73507353
}
73517354
}
@@ -9909,32 +9912,32 @@ namespace ts {
99099912
return (symbol.flags & SymbolFlags.ConstEnum) !== 0;
99109913
}
99119914

9912-
function checkInstanceOfExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type {
9915+
function checkInstanceOfExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type {
99139916
// TypeScript 1.0 spec (April 2014): 4.15.4
99149917
// The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type,
99159918
// and the right operand to be of type Any or a subtype of the 'Function' interface type.
99169919
// The result is always of the Boolean primitive type.
99179920
// NOTE: do not raise error if leftType is unknown as related error was already reported
99189921
if (allConstituentTypesHaveKind(leftType, TypeFlags.Primitive)) {
9919-
error(node.left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
9922+
error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
99209923
}
99219924
// NOTE: do not raise error if right is unknown as related error was already reported
99229925
if (!(isTypeAny(rightType) || isTypeSubtypeOf(rightType, globalFunctionType))) {
9923-
error(node.right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type);
9926+
error(right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type);
99249927
}
99259928
return booleanType;
99269929
}
99279930

9928-
function checkInExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type {
9931+
function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type {
99299932
// TypeScript 1.0 spec (April 2014): 4.15.5
99309933
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
99319934
// and the right operand to be of type Any, an object type, or a type parameter type.
99329935
// The result is always of the Boolean primitive type.
99339936
if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
9934-
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
9937+
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
99359938
}
99369939
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.ObjectType | TypeFlags.TypeParameter)) {
9937-
error(node.right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
9940+
error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
99389941
}
99399942
return booleanType;
99409943
}
@@ -9951,7 +9954,12 @@ namespace ts {
99519954
isNumericLiteralName(name.text) && getIndexTypeOfType(sourceType, IndexKind.Number) ||
99529955
getIndexTypeOfType(sourceType, IndexKind.String);
99539956
if (type) {
9954-
checkDestructuringAssignment((<PropertyAssignment>p).initializer || name, type);
9957+
if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
9958+
checkDestructuringAssignment(<ShorthandPropertyAssignment>p, type);
9959+
}
9960+
else {
9961+
checkDestructuringAssignment((<PropertyAssignment>p).initializer || name, type);
9962+
}
99559963
}
99569964
else {
99579965
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(sourceType), declarationNameToString(name));
@@ -10011,7 +10019,19 @@ namespace ts {
1001110019
return sourceType;
1001210020
}
1001310021

10014-
function checkDestructuringAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type {
10022+
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, contextualMapper?: TypeMapper): Type {
10023+
let target: Expression;
10024+
if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) {
10025+
const prop = <ShorthandPropertyAssignment>exprOrAssignment;
10026+
if (prop.objectAssignmentInitializer) {
10027+
checkBinaryLikeExpression(prop.name, prop.equalsToken, prop.objectAssignmentInitializer, contextualMapper);
10028+
}
10029+
target = (<ShorthandPropertyAssignment>exprOrAssignment).name;
10030+
}
10031+
else {
10032+
target = <Expression>exprOrAssignment;
10033+
}
10034+
1001510035
if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
1001610036
checkBinaryExpression(<BinaryExpression>target, contextualMapper);
1001710037
target = (<BinaryExpression>target).left;
@@ -10034,12 +10054,16 @@ namespace ts {
1003410054
}
1003510055

1003610056
function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) {
10037-
let operator = node.operatorToken.kind;
10038-
if (operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
10039-
return checkDestructuringAssignment(node.left, checkExpression(node.right, contextualMapper), contextualMapper);
10057+
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, contextualMapper, node);
10058+
}
10059+
10060+
function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, contextualMapper?: TypeMapper, errorNode?: Node) {
10061+
let operator = operatorToken.kind;
10062+
if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) {
10063+
return checkDestructuringAssignment(left, checkExpression(right, contextualMapper), contextualMapper);
1004010064
}
10041-
let leftType = checkExpression(node.left, contextualMapper);
10042-
let rightType = checkExpression(node.right, contextualMapper);
10065+
let leftType = checkExpression(left, contextualMapper);
10066+
let rightType = checkExpression(right, contextualMapper);
1004310067
switch (operator) {
1004410068
case SyntaxKind.AsteriskToken:
1004510069
case SyntaxKind.AsteriskEqualsToken:
@@ -10075,13 +10099,13 @@ namespace ts {
1007510099
// try and return them a helpful suggestion
1007610100
if ((leftType.flags & TypeFlags.Boolean) &&
1007710101
(rightType.flags & TypeFlags.Boolean) &&
10078-
(suggestedOperator = getSuggestedBooleanOperator(node.operatorToken.kind)) !== undefined) {
10079-
error(node, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(node.operatorToken.kind), tokenToString(suggestedOperator));
10102+
(suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) {
10103+
error(errorNode || operatorToken, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(operatorToken.kind), tokenToString(suggestedOperator));
1008010104
}
1008110105
else {
1008210106
// otherwise just check each operand separately and report errors as normal
10083-
let leftOk = checkArithmeticOperandType(node.left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
10084-
let rightOk = checkArithmeticOperandType(node.right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
10107+
let leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
10108+
let rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
1008510109
if (leftOk && rightOk) {
1008610110
checkAssignmentOperator(numberType);
1008710111
}
@@ -10147,9 +10171,9 @@ namespace ts {
1014710171
}
1014810172
return booleanType;
1014910173
case SyntaxKind.InstanceOfKeyword:
10150-
return checkInstanceOfExpression(node, leftType, rightType);
10174+
return checkInstanceOfExpression(left, right, leftType, rightType);
1015110175
case SyntaxKind.InKeyword:
10152-
return checkInExpression(node, leftType, rightType);
10176+
return checkInExpression(left, right, leftType, rightType);
1015310177
case SyntaxKind.AmpersandAmpersandToken:
1015410178
return rightType;
1015510179
case SyntaxKind.BarBarToken:
@@ -10164,8 +10188,8 @@ namespace ts {
1016410188
// Return true if there was no error, false if there was an error.
1016510189
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
1016610190
let offendingSymbolOperand =
10167-
someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? node.left :
10168-
someConstituentTypeHasKind(rightType, TypeFlags.ESSymbol) ? node.right :
10191+
someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? left :
10192+
someConstituentTypeHasKind(rightType, TypeFlags.ESSymbol) ? right :
1016910193
undefined;
1017010194
if (offendingSymbolOperand) {
1017110195
error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator));
@@ -10199,17 +10223,17 @@ namespace ts {
1019910223
// requires VarExpr to be classified as a reference
1020010224
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
1020110225
// and the type of the non - compound operation to be assignable to the type of VarExpr.
10202-
let ok = checkReferenceExpression(node.left, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant);
10226+
let ok = checkReferenceExpression(left, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant);
1020310227
// Use default messages
1020410228
if (ok) {
1020510229
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
10206-
checkTypeAssignableTo(valueType, leftType, node.left, /*headMessage*/ undefined);
10230+
checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined);
1020710231
}
1020810232
}
1020910233
}
1021010234

1021110235
function reportOperatorError() {
10212-
error(node, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(node.operatorToken.kind), typeToString(leftType), typeToString(rightType));
10236+
error(errorNode || operatorToken, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(operatorToken.kind), typeToString(leftType), typeToString(rightType));
1021310237
}
1021410238
}
1021510239

@@ -15421,7 +15445,7 @@ namespace ts {
1542115445
}
1542215446
}
1542315447

15424-
function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression) {
15448+
function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) {
1542515449
let seen: Map<SymbolFlags> = {};
1542615450
let Property = 1;
1542715451
let GetAccessor = 2;
@@ -15437,6 +15461,12 @@ namespace ts {
1543715461
continue;
1543815462
}
1543915463

15464+
if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && (<ShorthandPropertyAssignment>prop).objectAssignmentInitializer) {
15465+
// having objectAssignmentInitializer is only valid in ObjectAssignmentPattern
15466+
// outside of destructuring it is a syntax error
15467+
return grammarErrorOnNode((<ShorthandPropertyAssignment>prop).equalsToken, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment);
15468+
}
15469+
1544015470
// ECMA-262 11.1.5 Object Initialiser
1544115471
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
1544215472
// a.This production is contained in strict code and IsDataDescriptor(previous) is true and

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,10 @@
800800
"category": "Error",
801801
"code": 1311
802802
},
803+
"'=' can only be used in an object literal property inside a destructuring assignment.": {
804+
"category": "Error",
805+
"code": 1312
806+
},
803807
"Duplicate identifier '{0}'.": {
804808
"category": "Error",
805809
"code": 2300

src/compiler/emitter.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
23112311
write(": ");
23122312
emit(node.name);
23132313
}
2314+
2315+
if (languageVersion >= ScriptTarget.ES6 && node.objectAssignmentInitializer) {
2316+
write(" = ");
2317+
emit(node.objectAssignmentInitializer);
2318+
}
23142319
}
23152320

23162321
function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean {
@@ -3574,7 +3579,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
35743579
for (let p of properties) {
35753580
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
35763581
let propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
3577-
emitDestructuringAssignment((<PropertyAssignment>p).initializer || propName, createPropertyAccessForDestructuringProperty(value, propName));
3582+
let target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName;
3583+
emitDestructuringAssignment(target, createPropertyAccessForDestructuringProperty(value, propName));
35783584
}
35793585
}
35803586
}
@@ -3599,8 +3605,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
35993605
}
36003606
}
36013607

3602-
function emitDestructuringAssignment(target: Expression, value: Expression) {
3603-
if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
3608+
function emitDestructuringAssignment(target: Expression | ShorthandPropertyAssignment, value: Expression) {
3609+
if (target.kind === SyntaxKind.ShorthandPropertyAssignment) {
3610+
if ((<ShorthandPropertyAssignment>target).objectAssignmentInitializer) {
3611+
value = createDefaultValueCheck(value, (<ShorthandPropertyAssignment>target).objectAssignmentInitializer);
3612+
}
3613+
target = (<ShorthandPropertyAssignment>target).name;
3614+
}
3615+
else if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
36043616
value = createDefaultValueCheck(value, (<BinaryExpression>target).right);
36053617
target = (<BinaryExpression>target).left;
36063618
}

0 commit comments

Comments
 (0)