Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type guard narrows type any in a primitive type check #1621

Merged
merged 1 commit into from
Jan 8, 2015
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
}