Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -4646,21 +4646,16 @@ module ts {
if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) {
return type;
}

var left = <TypeOfExpression>expr.left;
var right = <LiteralExpression>expr.right;
if (left.expression.kind !== SyntaxKind.Identifier ||
getResolvedSymbol(<Identifier>left.expression) !== symbol) {

if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>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.
Expand Down Expand Up @@ -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(<Identifier>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(<Identifier>expr.left) !== symbol) {
return type;
}
// Check that right operand is a function type with a prototype property
Expand Down
2 changes: 0 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1331,8 +1331,6 @@ module ts {
// Generic class and interface types
export interface GenericType extends InterfaceType, TypeReference {
instantiations: Map<TypeReference>; // Generic instantiation cache
openReferenceTargets: GenericType[]; // Open type reference targets
openReferenceChecks: Map<boolean>; // Open type reference check cache
}

export interface TupleType extends ObjectType {
Expand Down
49 changes: 49 additions & 0 deletions tests/baselines/reference/typeGuardsWithAny.errors.txt
Original file line number Diff line number Diff line change
@@ -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
}

61 changes: 57 additions & 4 deletions tests/baselines/reference/typeGuardsWithAny.js
Original file line number Diff line number Diff line change
@@ -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
}
23 changes: 0 additions & 23 deletions tests/baselines/reference/typeGuardsWithAny.types

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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
}