From 7c09b724dc18c4937b08e8f910a0e37d50190b88 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Dec 2014 10:16:51 -0800 Subject: [PATCH 1/3] Type guards do no affect values of type any --- src/compiler/checker.ts | 8 ++++---- src/compiler/types.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8c383368df2a6..2d377350c65f9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6609,11 +6609,11 @@ module ts { // and the right operand to be of type Any or a subtype of the 'Function' interface type. // The result is always of the Boolean primitive type. // NOTE: do not raise error if leftType is unknown as related error was already reported - if (leftType !== unknownType && !isStructuredType(leftType)) { + if (!(leftType.flags & TypeFlags.Any || isStructuredType(leftType))) { error(node.left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } // NOTE: do not raise error if right is unknown as related error was already reported - if (rightType !== unknownType && rightType !== anyType && !isTypeSubtypeOf(rightType, globalFunctionType)) { + if (!(rightType.flags & TypeFlags.Any || isTypeSubtypeOf(rightType, globalFunctionType))) { 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); } return booleanType; @@ -6627,7 +6627,7 @@ module ts { if (leftType !== anyType && leftType !== stringType && leftType !== numberType) { error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number); } - if (!isStructuredType(rightType)) { + if (!(rightType.flags & TypeFlags.Any || isStructuredType(rightType))) { error(node.right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } return booleanType; @@ -7975,7 +7975,7 @@ module ts { var exprType = checkExpression(node.expression); // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved // in this case error about missing name is already reported - do not report extra one - if (!isStructuredType(exprType) && exprType !== unknownType) { + if (!(exprType.flags & TypeFlags.Any || isStructuredType(exprType))) { error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 19baf05240683..844f097e25038 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1240,7 +1240,7 @@ module ts { StringLike = String | StringLiteral, NumberLike = Number | Enum, ObjectType = Class | Interface | Reference | Tuple | Anonymous, - Structured = Any | ObjectType | Union | TypeParameter + Structured = ObjectType | Union | TypeParameter } // Properties common to all types From 37b5c74b93ee3a85d1e7b79b3da99e49950d5bd9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Dec 2014 10:17:48 -0800 Subject: [PATCH 2/3] Adding test case --- tests/baselines/reference/typeGuardsWithAny.js | 12 ++++++++++++ .../baselines/reference/typeGuardsWithAny.types | 17 +++++++++++++++++ .../expressions/typeGuards/typeGuardsWithAny.ts | 4 ++++ 3 files changed, 33 insertions(+) create mode 100644 tests/baselines/reference/typeGuardsWithAny.js create mode 100644 tests/baselines/reference/typeGuardsWithAny.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts diff --git a/tests/baselines/reference/typeGuardsWithAny.js b/tests/baselines/reference/typeGuardsWithAny.js new file mode 100644 index 0000000000000..628ea4251858a --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.js @@ -0,0 +1,12 @@ +//// [typeGuardsWithAny.ts] +var x: any = { p: 0 }; +if (x instanceof Object) { + x.p; // No error, type any is not narrowed +} + + +//// [typeGuardsWithAny.js] +var x = { p: 0 }; +if (x instanceof Object) { + x.p; // No error, type any is not narrowed +} diff --git a/tests/baselines/reference/typeGuardsWithAny.types b/tests/baselines/reference/typeGuardsWithAny.types new file mode 100644 index 0000000000000..d79f813715b42 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts === +var x: any = { p: 0 }; +>x : any +>{ p: 0 } : { p: number; } +>p : number + +if (x instanceof Object) { +>x instanceof Object : boolean +>x : any +>Object : ObjectConstructor + + x.p; // No error, type any is not narrowed +>x.p : any +>x : any +>p : any +} + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts new file mode 100644 index 0000000000000..4e58d1a4acc2d --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts @@ -0,0 +1,4 @@ +var x: any = { p: 0 }; +if (x instanceof Object) { + x.p; // No error, type any is not narrowed +} From 2876ba6a6cb1c133235b25e8a9d6f7417ba07a6e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Dec 2014 14:25:02 -0800 Subject: [PATCH 3/3] Addressing CR feedback --- src/compiler/checker.ts | 8 ++++---- src/compiler/types.ts | 1 - tests/baselines/reference/typeGuardsWithAny.js | 10 ++++++++-- tests/baselines/reference/typeGuardsWithAny.types | 8 +++++++- .../expressions/typeGuards/typeGuardsWithAny.ts | 5 ++++- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2d377350c65f9..1e8a87bc4d464 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4622,8 +4622,8 @@ module ts { // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { var type = getTypeOfSymbol(symbol); - // Only narrow when symbol is variable of a structured type - if (node && (symbol.flags & SymbolFlags.Variable && type.flags & TypeFlags.Structured)) { + // Only narrow when symbol is variable of an object, union, or type parameter type + if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { loop: while (node.parent) { var child = node; node = node.parent; @@ -6587,12 +6587,12 @@ module ts { return numberType; } - // Return true if type is any, an object type, a type parameter, or a union type composed of only those kinds of types + // Return true if type an object type, a type parameter, or a union type composed of only those kinds of types function isStructuredType(type: Type): boolean { if (type.flags & TypeFlags.Union) { return !forEach((type).types, t => !isStructuredType(t)); } - return (type.flags & TypeFlags.Structured) !== 0; + return (type.flags & (TypeFlags.ObjectType | TypeFlags.TypeParameter)) !== 0; } function isConstEnumObjectType(type: Type): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 844f097e25038..7d68d4b5a000d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1240,7 +1240,6 @@ module ts { StringLike = String | StringLiteral, NumberLike = Number | Enum, ObjectType = Class | Interface | Reference | Tuple | Anonymous, - Structured = ObjectType | Union | TypeParameter } // Properties common to all types diff --git a/tests/baselines/reference/typeGuardsWithAny.js b/tests/baselines/reference/typeGuardsWithAny.js index 628ea4251858a..56676fb6d6884 100644 --- a/tests/baselines/reference/typeGuardsWithAny.js +++ b/tests/baselines/reference/typeGuardsWithAny.js @@ -1,12 +1,18 @@ //// [typeGuardsWithAny.ts] var x: any = { p: 0 }; if (x instanceof Object) { - x.p; // No error, type any is not narrowed + x.p; // No error, type any unaffected by type guard +} +else { + x.p; // No error, type any unaffected by type guard } //// [typeGuardsWithAny.js] var x = { p: 0 }; if (x instanceof Object) { - x.p; // No error, type any is not narrowed + x.p; // No error, type any unaffected by type guard +} +else { + x.p; // No error, type any unaffected by type guard } diff --git a/tests/baselines/reference/typeGuardsWithAny.types b/tests/baselines/reference/typeGuardsWithAny.types index d79f813715b42..ee628c9c2497d 100644 --- a/tests/baselines/reference/typeGuardsWithAny.types +++ b/tests/baselines/reference/typeGuardsWithAny.types @@ -9,7 +9,13 @@ if (x instanceof Object) { >x : any >Object : ObjectConstructor - x.p; // No error, type any is not narrowed + x.p; // No error, type any unaffected by type guard +>x.p : any +>x : any +>p : any +} +else { + x.p; // No error, type any unaffected by type guard >x.p : any >x : any >p : any diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts index 4e58d1a4acc2d..e7e756ea8e041 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts @@ -1,4 +1,7 @@ var x: any = { p: 0 }; if (x instanceof Object) { - x.p; // No error, type any is not narrowed + x.p; // No error, type any unaffected by type guard +} +else { + x.p; // No error, type any unaffected by type guard }