diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2911322052da3..96fab3368ca0c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4589,8 +4589,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 an object, union, or type parameter type - if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { + // Only narrow when symbol is variable of type any or an object, union, or type parameter type + if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { loop: while (node.parent) { var child = node; node = node.parent; @@ -4646,21 +4646,16 @@ module ts { if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) { return type; } - var left = expr.left; var right = expr.right; - if (left.expression.kind !== SyntaxKind.Identifier || - getResolvedSymbol(left.expression) !== symbol) { - + if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(left.expression) !== symbol) { return type; } - var t = right.text; var checkType: Type = t === "string" ? stringType : t === "number" ? numberType : t === "boolean" ? booleanType : emptyObjectType; if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (assumeTrue) { // The assumed result is true. If check was for a primitive type, that type is the narrowed type. Otherwise we can // remove the primitive types from the narrowed type. @@ -4704,8 +4699,8 @@ module ts { } function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - // Check that assumed result is true and we have variable symbol on the left - if (!assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) { + // Check that type is not any, assumed result is true, and we have variable symbol on the left + if (type.flags & TypeFlags.Any || !assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) { return type; } // Check that right operand is a function type with a prototype property diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 60ed8352b9a3d..9ebab8dc2207c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1331,8 +1331,6 @@ module ts { // Generic class and interface types export interface GenericType extends InterfaceType, TypeReference { instantiations: Map; // Generic instantiation cache - openReferenceTargets: GenericType[]; // Open type reference targets - openReferenceChecks: Map; // Open type reference check cache } export interface TupleType extends ObjectType { diff --git a/tests/baselines/reference/typeGuardsWithAny.errors.txt b/tests/baselines/reference/typeGuardsWithAny.errors.txt new file mode 100644 index 0000000000000..653c89c7554ad --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.errors.txt @@ -0,0 +1,49 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(11,7): error TS2339: Property 'p' does not exist on type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(18,7): error TS2339: Property 'p' does not exist on type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(25,7): error TS2339: Property 'p' does not exist on type 'boolean'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts (3 errors) ==== + var x: any = { p: 0 }; + + if (x instanceof Object) { + x.p; // No error, type any unaffected by instanceof type guard + } + else { + x.p; // No error, type any unaffected by instanceof type guard + } + + if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'string'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'number'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'boolean'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check + } + else { + x.p; // No error, type unaffected in this branch + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithAny.js b/tests/baselines/reference/typeGuardsWithAny.js index 56676fb6d6884..be3d64b806fc1 100644 --- a/tests/baselines/reference/typeGuardsWithAny.js +++ b/tests/baselines/reference/typeGuardsWithAny.js @@ -1,18 +1,71 @@ //// [typeGuardsWithAny.ts] var x: any = { p: 0 }; + if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} + +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check +} +else { + x.p; // No error, type unaffected in this branch } //// [typeGuardsWithAny.js] var x = { p: 0 }; if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} +else { + x.p; // No error, type any unaffected by instanceof type guard +} +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type unaffected in this branch } diff --git a/tests/baselines/reference/typeGuardsWithAny.types b/tests/baselines/reference/typeGuardsWithAny.types deleted file mode 100644 index ee628c9c2497d..0000000000000 --- a/tests/baselines/reference/typeGuardsWithAny.types +++ /dev/null @@ -1,23 +0,0 @@ -=== 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 -} - diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts index e7e756ea8e041..a6379bf95b082 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts @@ -1,7 +1,36 @@ var x: any = { p: 0 }; + if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} + +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check +} +else { + x.p; // No error, type unaffected in this branch }