Merge pull request #1621 from Microsoft/narrowingOfAny

Type guard narrows type any in a primitive type check
This commit is contained in:
Anders Hejlsberg 2015-01-08 15:57:15 -08:00
commit 40dc1341ab
6 changed files with 142 additions and 41 deletions

View file

@ -4590,8 +4590,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;
@ -4647,21 +4647,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.
@ -4705,8 +4700,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

View file

@ -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 {

View file

@ -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
}

View file

@ -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 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
}

View file

@ -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
}

View file

@ -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
}