Skip to content

Commit

Permalink
Merge pull request #1433 from Microsoft/typeGuardWithAny
Browse files Browse the repository at this point in the history
Type guards should not affect values of type any
  • Loading branch information
ahejlsberg committed Dec 10, 2014
2 parents f51767c + 2876ba6 commit ab4706a
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 9 deletions.
16 changes: 8 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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((<UnionType>type).types, t => !isStructuredType(t));
}
return (type.flags & TypeFlags.Structured) !== 0;
return (type.flags & (TypeFlags.ObjectType | TypeFlags.TypeParameter)) !== 0;
}

function isConstEnumObjectType(type: Type): boolean {
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
1 change: 0 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,6 @@ module ts {
StringLike = String | StringLiteral,
NumberLike = Number | Enum,
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
Structured = Any | ObjectType | Union | TypeParameter
}

// Properties common to all types
Expand Down
18 changes: 18 additions & 0 deletions tests/baselines/reference/typeGuardsWithAny.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//// [typeGuardsWithAny.ts]
var x: any = { p: 0 };
if (x instanceof Object) {
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 unaffected by type guard
}
else {
x.p; // No error, type any unaffected by type guard
}
23 changes: 23 additions & 0 deletions tests/baselines/reference/typeGuardsWithAny.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
=== 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 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
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var x: any = { p: 0 };
if (x instanceof Object) {
x.p; // No error, type any unaffected by type guard
}
else {
x.p; // No error, type any unaffected by type guard
}

0 comments on commit ab4706a

Please sign in to comment.