Merge pull request #1621 from Microsoft/narrowingOfAny
Type guard narrows type any in a primitive type check
This commit is contained in:
commit
40dc1341ab
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
49
tests/baselines/reference/typeGuardsWithAny.errors.txt
Normal file
49
tests/baselines/reference/typeGuardsWithAny.errors.txt
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue