Merge pull request #10334 from yortus/narrow-from-any

Narrow from 'any' in most situations
This commit is contained in:
Daniel Rosenwasser 2016-08-14 22:46:47 -07:00 committed by GitHub
commit 78cea2adb5
13 changed files with 458 additions and 30 deletions

View file

@ -8548,10 +8548,6 @@ namespace ts {
}
return type;
}
// We never narrow type any in an instanceof guard
if (isTypeAny(type)) {
return type;
}
// Check that right operand is a function type with a prototype property
const rightType = checkExpression(expr.right);
@ -8569,6 +8565,11 @@ namespace ts {
}
}
// Don't narrow from 'any' if the target type is exactly 'Object' or 'Function'
if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) {
return type;
}
if (!targetType) {
// Target type is type of construct signature
let constructSignatures: Signature[];
@ -8615,7 +8616,7 @@ namespace ts {
}
function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
if (type.flags & TypeFlags.Any || !hasMatchingArgument(callExpression, reference)) {
if (!hasMatchingArgument(callExpression, reference)) {
return type;
}
const signature = getResolvedSignature(callExpression);
@ -8623,6 +8624,12 @@ namespace ts {
if (!predicate) {
return type;
}
// Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function'
if (isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType)) {
return type;
}
if (isIdentifierTypePredicate(predicate)) {
const predicateArgument = callExpression.arguments[predicate.parameterIndex];
if (predicateArgument) {

View file

@ -0,0 +1,33 @@
tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts(11,17): error TS2339: Property 'doPanic' does not exist on type '{ type: "foo"; dontPanic(): any; }'.
tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts(16,17): error TS2339: Property 'massage' does not exist on type 'Error'.
==== tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts (2 errors) ====
declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); };
function tryCatch() {
try {
// do stuff...
}
catch (err) { // err is implicitly 'any' and cannot be annotated
if (isFooError(err)) {
err.dontPanic(); // OK
err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}'
~~~~~~~
!!! error TS2339: Property 'doPanic' does not exist on type '{ type: "foo"; dontPanic(): any; }'.
}
else if (err instanceof Error) {
err.message;
err.massage; // ERROR: Property 'massage' does not exist on type 'Error'
~~~~~~~
!!! error TS2339: Property 'massage' does not exist on type 'Error'.
}
else {
throw err;
}
}
}

View file

@ -0,0 +1,44 @@
//// [narrowExceptionVariableInCatchClause.ts]
declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); };
function tryCatch() {
try {
// do stuff...
}
catch (err) { // err is implicitly 'any' and cannot be annotated
if (isFooError(err)) {
err.dontPanic(); // OK
err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}'
}
else if (err instanceof Error) {
err.message;
err.massage; // ERROR: Property 'massage' does not exist on type 'Error'
}
else {
throw err;
}
}
}
//// [narrowExceptionVariableInCatchClause.js]
function tryCatch() {
try {
}
catch (err) {
if (isFooError(err)) {
err.dontPanic(); // OK
err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}'
}
else if (err instanceof Error) {
err.message;
err.massage; // ERROR: Property 'massage' does not exist on type 'Error'
}
else {
throw err;
}
}
}

View file

@ -0,0 +1,33 @@
tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts(17,7): error TS2339: Property 'mesage' does not exist on type 'Error'.
tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts(22,7): error TS2339: Property 'getHuors' does not exist on type 'Date'.
==== tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts (2 errors) ====
declare var x: any;
if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function'
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object'
x.method();
x();
}
if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object'
x.message;
x.mesage;
~~~~~~
!!! error TS2339: Property 'mesage' does not exist on type 'Error'.
}
if (x instanceof Date) {
x.getDate();
x.getHuors();
~~~~~~~~
!!! error TS2339: Property 'getHuors' does not exist on type 'Date'.
}

View file

@ -0,0 +1,45 @@
//// [narrowFromAnyWithInstanceof.ts]
declare var x: any;
if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function'
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object'
x.method();
x();
}
if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object'
x.message;
x.mesage;
}
if (x instanceof Date) {
x.getDate();
x.getHuors();
}
//// [narrowFromAnyWithInstanceof.js]
if (x instanceof Function) {
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (x instanceof Object) {
x.method();
x();
}
if (x instanceof Error) {
x.message;
x.mesage;
}
if (x instanceof Date) {
x.getDate();
x.getHuors();
}

View file

@ -0,0 +1,50 @@
tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(22,7): error TS2339: Property 'method' does not exist on type '{}'.
tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(23,5): error TS2349: Cannot invoke an expression whose type lacks a call signature.
tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(28,7): error TS2339: Property 'mesage' does not exist on type 'Error'.
tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(33,7): error TS2339: Property 'getHuors' does not exist on type 'Date'.
==== tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts (4 errors) ====
declare var x: any;
declare function isFunction(x): x is Function;
declare function isObject(x): x is Object;
declare function isAnything(x): x is {};
declare function isError(x): x is Error;
declare function isDate(x): x is Date;
if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function'
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (isObject(x)) { // 'any' is not narrowed when target type is 'Object'
x.method();
x();
}
if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {})
x.method();
~~~~~~
!!! error TS2339: Property 'method' does not exist on type '{}'.
x();
~~~
!!! error TS2349: Cannot invoke an expression whose type lacks a call signature.
}
if (isError(x)) {
x.message;
x.mesage;
~~~~~~
!!! error TS2339: Property 'mesage' does not exist on type 'Error'.
}
if (isDate(x)) {
x.getDate();
x.getHuors();
~~~~~~~~
!!! error TS2339: Property 'getHuors' does not exist on type 'Date'.
}

View file

@ -0,0 +1,60 @@
//// [narrowFromAnyWithTypePredicate.ts]
declare var x: any;
declare function isFunction(x): x is Function;
declare function isObject(x): x is Object;
declare function isAnything(x): x is {};
declare function isError(x): x is Error;
declare function isDate(x): x is Date;
if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function'
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (isObject(x)) { // 'any' is not narrowed when target type is 'Object'
x.method();
x();
}
if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {})
x.method();
x();
}
if (isError(x)) {
x.message;
x.mesage;
}
if (isDate(x)) {
x.getDate();
x.getHuors();
}
//// [narrowFromAnyWithTypePredicate.js]
if (isFunction(x)) {
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (isObject(x)) {
x.method();
x();
}
if (isAnything(x)) {
x.method();
x();
}
if (isError(x)) {
x.message;
x.mesage;
}
if (isDate(x)) {
x.getDate();
x.getHuors();
}

View file

@ -1,16 +1,26 @@
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(12,10): error TS2339: Property 'bar' does not exist on type 'A'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(18,10): error TS2339: Property 'bar' does not exist on type 'A'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(33,5): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(34,10): error TS2339: Property 'bar' does not exist on type 'B<number>'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(41,10): error TS2339: Property 'bar' does not exist on type 'B<any>'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(72,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(73,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(85,10): error TS2339: Property 'bar' does not exist on type 'D'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(91,10): error TS2339: Property 'bar' does not exist on type 'D'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(118,11): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(187,11): error TS2339: Property 'foo1' does not exist on type 'H'.
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2339: Property 'foo2' does not exist on type 'H'.
==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (10 errors) ====
==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ====
interface AConstructor {
new (): A;
}
@ -28,9 +38,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj2: any;
if (obj2 instanceof A) { // can't narrow type from 'any'
if (obj2 instanceof A) {
obj2.foo;
obj2.bar;
~~~
!!! error TS2339: Property 'bar' does not exist on type 'A'.
}
// a construct signature with generics
@ -54,10 +66,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj4: any;
if (obj4 instanceof B) { // can't narrow type from 'any'
if (obj4 instanceof B) {
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
~~~
!!! error TS2339: Property 'bar' does not exist on type 'B<any>'.
}
// has multiple construct signature
@ -88,10 +102,14 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj6: any;
if (obj6 instanceof C) { // can't narrow type from 'any'
if (obj6 instanceof C) {
obj6.foo;
obj6.bar1;
~~~~
!!! error TS2339: Property 'bar1' does not exist on type 'C1 | C2'.
obj6.bar2;
~~~~
!!! error TS2339: Property 'bar2' does not exist on type 'C1 | C2'.
}
// with object type literal
@ -109,9 +127,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj8: any;
if (obj8 instanceof D) { // can't narrow type from 'any'
if (obj8 instanceof D) {
obj8.foo;
obj8.bar;
~~~
!!! error TS2339: Property 'bar' does not exist on type 'D'.
}
// a construct signature that returns a union type
@ -138,10 +158,14 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj10: any;
if (obj10 instanceof E) { // can't narrow type from 'any'
if (obj10 instanceof E) {
obj10.foo;
obj10.bar1;
~~~~
!!! error TS2339: Property 'bar1' does not exist on type 'E1 | E2'.
obj10.bar2;
~~~~
!!! error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
}
// a construct signature that returns any
@ -165,7 +189,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj12: any;
if (obj12 instanceof F) { // can't narrow type from 'any'
if (obj12 instanceof F) {
obj12.foo;
obj12.bar;
}
@ -192,9 +216,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj14: any;
if (obj14 instanceof G) { // can't narrow type from 'any'
if (obj14 instanceof G) {
obj14.foo1;
obj14.foo2;
~~~~
!!! error TS2339: Property 'foo2' does not exist on type 'G1'.
}
// a type with a prototype that has any type
@ -216,8 +242,24 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
}
var obj16: any;
if (obj16 instanceof H) { // can't narrow type from 'any'
if (obj16 instanceof H) {
obj16.foo1;
~~~~
!!! error TS2339: Property 'foo1' does not exist on type 'H'.
obj16.foo2;
~~~~
!!! error TS2339: Property 'foo2' does not exist on type 'H'.
}
var obj17: any;
if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object'
obj17.foo1;
obj17.foo2;
}
var obj18: any;
if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function'
obj18.foo1;
obj18.foo2;
}

View file

@ -14,7 +14,7 @@ if (obj1 instanceof A) { // narrowed to A.
}
var obj2: any;
if (obj2 instanceof A) { // can't narrow type from 'any'
if (obj2 instanceof A) {
obj2.foo;
obj2.bar;
}
@ -36,7 +36,7 @@ if (obj3 instanceof B) { // narrowed to B<number>.
}
var obj4: any;
if (obj4 instanceof B) { // can't narrow type from 'any'
if (obj4 instanceof B) {
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
@ -68,7 +68,7 @@ if (obj5 instanceof C) { // narrowed to C1|C2.
}
var obj6: any;
if (obj6 instanceof C) { // can't narrow type from 'any'
if (obj6 instanceof C) {
obj6.foo;
obj6.bar1;
obj6.bar2;
@ -87,7 +87,7 @@ if (obj7 instanceof D) { // narrowed to D.
}
var obj8: any;
if (obj8 instanceof D) { // can't narrow type from 'any'
if (obj8 instanceof D) {
obj8.foo;
obj8.bar;
}
@ -114,7 +114,7 @@ if (obj9 instanceof E) { // narrowed to E1 | E2
}
var obj10: any;
if (obj10 instanceof E) { // can't narrow type from 'any'
if (obj10 instanceof E) {
obj10.foo;
obj10.bar1;
obj10.bar2;
@ -137,7 +137,7 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a
}
var obj12: any;
if (obj12 instanceof F) { // can't narrow type from 'any'
if (obj12 instanceof F) {
obj12.foo;
obj12.bar;
}
@ -162,7 +162,7 @@ if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype prop
}
var obj14: any;
if (obj14 instanceof G) { // can't narrow type from 'any'
if (obj14 instanceof G) {
obj14.foo1;
obj14.foo2;
}
@ -184,10 +184,22 @@ if (obj15 instanceof H) { // narrowed to H.
}
var obj16: any;
if (obj16 instanceof H) { // can't narrow type from 'any'
if (obj16 instanceof H) {
obj16.foo1;
obj16.foo2;
}
var obj17: any;
if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object'
obj17.foo1;
obj17.foo2;
}
var obj18: any;
if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function'
obj18.foo1;
obj18.foo2;
}
//// [typeGuardsWithInstanceOfByConstructorSignature.js]
@ -278,3 +290,13 @@ if (obj16 instanceof H) {
obj16.foo1;
obj16.foo2;
}
var obj17;
if (obj17 instanceof Object) {
obj17.foo1;
obj17.foo2;
}
var obj18;
if (obj18 instanceof Function) {
obj18.foo1;
obj18.foo2;
}

View file

@ -13,7 +13,7 @@ if (obj1 instanceof A) { // narrowed to A.
}
var obj2: any;
if (obj2 instanceof A) { // can't narrow type from 'any'
if (obj2 instanceof A) {
obj2.foo;
obj2.bar;
}
@ -35,7 +35,7 @@ if (obj3 instanceof B) { // narrowed to B<number>.
}
var obj4: any;
if (obj4 instanceof B) { // can't narrow type from 'any'
if (obj4 instanceof B) {
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
@ -67,7 +67,7 @@ if (obj5 instanceof C) { // narrowed to C1|C2.
}
var obj6: any;
if (obj6 instanceof C) { // can't narrow type from 'any'
if (obj6 instanceof C) {
obj6.foo;
obj6.bar1;
obj6.bar2;
@ -86,7 +86,7 @@ if (obj7 instanceof D) { // narrowed to D.
}
var obj8: any;
if (obj8 instanceof D) { // can't narrow type from 'any'
if (obj8 instanceof D) {
obj8.foo;
obj8.bar;
}
@ -113,7 +113,7 @@ if (obj9 instanceof E) { // narrowed to E1 | E2
}
var obj10: any;
if (obj10 instanceof E) { // can't narrow type from 'any'
if (obj10 instanceof E) {
obj10.foo;
obj10.bar1;
obj10.bar2;
@ -136,7 +136,7 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a
}
var obj12: any;
if (obj12 instanceof F) { // can't narrow type from 'any'
if (obj12 instanceof F) {
obj12.foo;
obj12.bar;
}
@ -161,7 +161,7 @@ if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype prop
}
var obj14: any;
if (obj14 instanceof G) { // can't narrow type from 'any'
if (obj14 instanceof G) {
obj14.foo1;
obj14.foo2;
}
@ -183,7 +183,19 @@ if (obj15 instanceof H) { // narrowed to H.
}
var obj16: any;
if (obj16 instanceof H) { // can't narrow type from 'any'
if (obj16 instanceof H) {
obj16.foo1;
obj16.foo2;
}
var obj17: any;
if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object'
obj17.foo1;
obj17.foo2;
}
var obj18: any;
if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function'
obj18.foo1;
obj18.foo2;
}

View file

@ -0,0 +1,23 @@
declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); };
function tryCatch() {
try {
// do stuff...
}
catch (err) { // err is implicitly 'any' and cannot be annotated
if (isFooError(err)) {
err.dontPanic(); // OK
err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}'
}
else if (err instanceof Error) {
err.message;
err.massage; // ERROR: Property 'massage' does not exist on type 'Error'
}
else {
throw err;
}
}
}

View file

@ -0,0 +1,23 @@
declare var x: any;
if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function'
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object'
x.method();
x();
}
if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object'
x.message;
x.mesage;
}
if (x instanceof Date) {
x.getDate();
x.getHuors();
}

View file

@ -0,0 +1,34 @@
declare var x: any;
declare function isFunction(x): x is Function;
declare function isObject(x): x is Object;
declare function isAnything(x): x is {};
declare function isError(x): x is Error;
declare function isDate(x): x is Date;
if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function'
x();
x(1, 2, 3);
x("hello!");
x.prop;
}
if (isObject(x)) { // 'any' is not narrowed when target type is 'Object'
x.method();
x();
}
if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {})
x.method();
x();
}
if (isError(x)) {
x.message;
x.mesage;
}
if (isDate(x)) {
x.getDate();
x.getHuors();
}