Skip to content

Commit 2d16474

Browse files
committed
Fix expression checking for symbols
1 parent d793658 commit 2d16474

24 files changed

+484
-44
lines changed

src/compiler/checker.ts

+59-13
Original file line numberDiff line numberDiff line change
@@ -6856,6 +6856,9 @@ module ts {
68566856
case SyntaxKind.PlusToken:
68576857
case SyntaxKind.MinusToken:
68586858
case SyntaxKind.TildeToken:
6859+
if (hasSomeTypeOfKind(operandType, TypeFlags.ESSymbol)) {
6860+
error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_a_value_of_type_symbol, tokenToString(node.operator));
6861+
}
68596862
return numberType;
68606863
case SyntaxKind.ExclamationToken:
68616864
return booleanType;
@@ -6891,6 +6894,24 @@ module ts {
68916894
return numberType;
68926895
}
68936896

6897+
// Just like isTypeOfKind below, except that it returns true if *any* constituent
6898+
// has this kind.
6899+
function hasSomeTypeOfKind(type: Type, kind: TypeFlags): boolean {
6900+
if (type.flags & kind) {
6901+
return true;
6902+
}
6903+
if (type.flags & TypeFlags.Union) {
6904+
var types = (<UnionType>type).types;
6905+
for (var i = 0; i < types.length; i++) {
6906+
if (types[i].flags & kind) {
6907+
return true;
6908+
}
6909+
}
6910+
return false;
6911+
}
6912+
return false;
6913+
}
6914+
68946915
// Return true if type has the given flags, or is a union type composed of types that all have those flags.
68956916
function isTypeOfKind(type: Type, kind: TypeFlags): boolean {
68966917
if (type.flags & kind) {
@@ -6938,7 +6959,7 @@ module ts {
69386959
// and the right operand to be of type Any, an object type, or a type parameter type.
69396960
// The result is always of the Boolean primitive type.
69406961
if (!isTypeOfKind(leftType, TypeFlags.Any | TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
6941-
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number);
6962+
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
69426963
}
69436964
if (!isTypeOfKind(rightType, TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.TypeParameter)) {
69446965
error(node.right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
@@ -7106,14 +7127,21 @@ module ts {
71067127
// If both operands are of the Number primitive type, the result is of the Number primitive type.
71077128
resultType = numberType;
71087129
}
7109-
else if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) {
7110-
// If one or both operands are of the String primitive type, the result is of the String primitive type.
7111-
resultType = stringType;
7112-
}
7113-
else if (leftType.flags & TypeFlags.Any || rightType.flags & TypeFlags.Any) {
7114-
// Otherwise, the result is of type Any.
7115-
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
7116-
resultType = anyType;
7130+
else {
7131+
if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) {
7132+
// If one or both operands are of the String primitive type, the result is of the String primitive type.
7133+
resultType = stringType;
7134+
}
7135+
else if (leftType.flags & TypeFlags.Any || rightType.flags & TypeFlags.Any) {
7136+
// Otherwise, the result is of type Any.
7137+
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
7138+
resultType = anyType;
7139+
}
7140+
7141+
// Symbols are not allowed at all in arithmetic expressions
7142+
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
7143+
return resultType;
7144+
}
71177145
}
71187146

71197147
if (!resultType) {
@@ -7125,14 +7153,18 @@ module ts {
71257153
checkAssignmentOperator(resultType);
71267154
}
71277155
return resultType;
7128-
case SyntaxKind.EqualsEqualsToken:
7129-
case SyntaxKind.ExclamationEqualsToken:
7130-
case SyntaxKind.EqualsEqualsEqualsToken:
7131-
case SyntaxKind.ExclamationEqualsEqualsToken:
71327156
case SyntaxKind.LessThanToken:
71337157
case SyntaxKind.GreaterThanToken:
71347158
case SyntaxKind.LessThanEqualsToken:
71357159
case SyntaxKind.GreaterThanEqualsToken:
7160+
if (!checkForDisallowedESSymbolOperand(operator)) {
7161+
return booleanType;
7162+
}
7163+
// Fall through
7164+
case SyntaxKind.EqualsEqualsToken:
7165+
case SyntaxKind.ExclamationEqualsToken:
7166+
case SyntaxKind.EqualsEqualsEqualsToken:
7167+
case SyntaxKind.ExclamationEqualsEqualsToken:
71367168
if (!isTypeAssignableTo(leftType, rightType) && !isTypeAssignableTo(rightType, leftType)) {
71377169
reportOperatorError();
71387170
}
@@ -7152,6 +7184,20 @@ module ts {
71527184
return rightType;
71537185
}
71547186

7187+
// Return type is true if there was no error, false if there was an error.
7188+
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
7189+
var offendingSymbolOperand =
7190+
hasSomeTypeOfKind(leftType, TypeFlags.ESSymbol) ? node.left :
7191+
hasSomeTypeOfKind(rightType, TypeFlags.ESSymbol) ? node.right :
7192+
undefined;
7193+
if (offendingSymbolOperand) {
7194+
error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_a_value_of_type_symbol, tokenToString(operator));
7195+
return false;
7196+
}
7197+
7198+
return true;
7199+
}
7200+
71557201
function getSuggestedBooleanOperator(operator: SyntaxKind): SyntaxKind {
71567202
switch (operator) {
71577203
case SyntaxKind.BarToken:

src/compiler/diagnosticInformationMap.generated.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ module ts {
205205
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer: { code: 2357, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator must be a variable, property or indexer." },
206206
The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2358, category: DiagnosticCategory.Error, key: "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter." },
207207
The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type: { code: 2359, category: DiagnosticCategory.Error, key: "The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type." },
208-
The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number: { code: 2360, category: DiagnosticCategory.Error, key: "The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'." },
208+
The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol: { code: 2360, category: DiagnosticCategory.Error, key: "The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'." },
209209
The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2361, category: DiagnosticCategory.Error, key: "The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter" },
210210
The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2362, category: DiagnosticCategory.Error, key: "The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
211211
The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2363, category: DiagnosticCategory.Error, key: "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
@@ -305,6 +305,7 @@ module ts {
305305
super_cannot_be_referenced_in_a_computed_property_name: { code: 2466, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in a computed property name." },
306306
A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type: { code: 2467, category: DiagnosticCategory.Error, key: "A computed property name cannot reference a type parameter from its containing type." },
307307
Cannot_find_global_value_0: { code: 2468, category: DiagnosticCategory.Error, key: "Cannot find global value '{0}'." },
308+
The_0_operator_cannot_be_applied_to_a_value_of_type_symbol: { code: 2469, category: DiagnosticCategory.Error, key: "The '{0}' operator cannot be applied to a value of type 'symbol'." },
308309
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
309310
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
310311
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@
813813
"category": "Error",
814814
"code": 2359
815815
},
816-
"The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": {
816+
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
817817
"category": "Error",
818818
"code": 2360
819819
},
@@ -1213,6 +1213,10 @@
12131213
"category": "Error",
12141214
"code": 2468
12151215
},
1216+
"The '{0}' operator cannot be applied to a value of type 'symbol'.": {
1217+
"category": "Error",
1218+
"code": 2469
1219+
},
12161220

12171221
"Import declaration '{0}' is using private name '{1}'.": {
12181222
"category": "Error",

tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(12,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
2-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(13,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
3-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(14,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
4-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
5-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
6-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
7-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
1+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(12,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
2+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(13,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
3+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(14,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
5+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
6+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
7+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
88
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(30,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
99
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(31,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
1010
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(32,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
@@ -15,7 +15,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
1515
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
1616
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(38,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
1717
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(39,17): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
18-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
18+
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
1919
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,17): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
2020

2121

@@ -33,27 +33,27 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
3333

3434
var ra1 = a1 in x;
3535
~~
36-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
36+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
3737
var ra2 = a2 in x;
3838
~~
39-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
39+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4040
var ra3 = a3 in x;
4141
~~
42-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
42+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4343
var ra4 = a4 in x;
4444
var ra5 = null in x;
4545
~~~~
46-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
46+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4747
var ra6 = undefined in x;
4848
~~~~~~~~~
49-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
49+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
5050
var ra7 = E.a in x;
5151
var ra8 = false in x;
5252
~~~~~
53-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
53+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
5454
var ra9 = {} in x;
5555
~~
56-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
56+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
5757

5858
// invalid right operands
5959
// the right operand is required to be of type Any, an object type, or a type parameter type
@@ -98,6 +98,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
9898
// both operands are invalid
9999
var rc1 = {} in '';
100100
~~
101-
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
101+
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
102102
~~
103103
!!! error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tests/cases/conformance/es6/Symbols/symbolProperty54.ts(2,5): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
2+
3+
4+
==== tests/cases/conformance/es6/Symbols/symbolProperty54.ts (1 errors) ====
5+
var obj = {
6+
[Symbol.prototype]: 0
7+
~~~~~~~~~~~~~~~~~~
8+
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
9+
};

0 commit comments

Comments
 (0)