Skip to content

Commit 40dc134

Browse files
committed
Merge pull request #1621 from Microsoft/narrowingOfAny
Type guard narrows type any in a primitive type check
2 parents 5562810 + 968a569 commit 40dc134

File tree

6 files changed

+142
-41
lines changed

6 files changed

+142
-41
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4590,8 +4590,8 @@ module ts {
45904590
// Get the narrowed type of a given symbol at a given location
45914591
function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) {
45924592
var type = getTypeOfSymbol(symbol);
4593-
// Only narrow when symbol is variable of an object, union, or type parameter type
4594-
if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) {
4593+
// Only narrow when symbol is variable of type any or an object, union, or type parameter type
4594+
if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) {
45954595
loop: while (node.parent) {
45964596
var child = node;
45974597
node = node.parent;
@@ -4647,21 +4647,16 @@ module ts {
46474647
if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) {
46484648
return type;
46494649
}
4650-
46514650
var left = <TypeOfExpression>expr.left;
46524651
var right = <LiteralExpression>expr.right;
4653-
if (left.expression.kind !== SyntaxKind.Identifier ||
4654-
getResolvedSymbol(<Identifier>left.expression) !== symbol) {
4655-
4652+
if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>left.expression) !== symbol) {
46564653
return type;
46574654
}
4658-
46594655
var t = right.text;
46604656
var checkType: Type = t === "string" ? stringType : t === "number" ? numberType : t === "boolean" ? booleanType : emptyObjectType;
46614657
if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) {
46624658
assumeTrue = !assumeTrue;
46634659
}
4664-
46654660
if (assumeTrue) {
46664661
// The assumed result is true. If check was for a primitive type, that type is the narrowed type. Otherwise we can
46674662
// remove the primitive types from the narrowed type.
@@ -4705,8 +4700,8 @@ module ts {
47054700
}
47064701

47074702
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
4708-
// Check that assumed result is true and we have variable symbol on the left
4709-
if (!assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>expr.left) !== symbol) {
4703+
// Check that type is not any, assumed result is true, and we have variable symbol on the left
4704+
if (type.flags & TypeFlags.Any || !assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>expr.left) !== symbol) {
47104705
return type;
47114706
}
47124707
// Check that right operand is a function type with a prototype property

src/compiler/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,8 +1331,6 @@ module ts {
13311331
// Generic class and interface types
13321332
export interface GenericType extends InterfaceType, TypeReference {
13331333
instantiations: Map<TypeReference>; // Generic instantiation cache
1334-
openReferenceTargets: GenericType[]; // Open type reference targets
1335-
openReferenceChecks: Map<boolean>; // Open type reference check cache
13361334
}
13371335

13381336
export interface TupleType extends ObjectType {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(11,7): error TS2339: Property 'p' does not exist on type 'string'.
2+
tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(18,7): error TS2339: Property 'p' does not exist on type 'number'.
3+
tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(25,7): error TS2339: Property 'p' does not exist on type 'boolean'.
4+
5+
6+
==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts (3 errors) ====
7+
var x: any = { p: 0 };
8+
9+
if (x instanceof Object) {
10+
x.p; // No error, type any unaffected by instanceof type guard
11+
}
12+
else {
13+
x.p; // No error, type any unaffected by instanceof type guard
14+
}
15+
16+
if (typeof x === "string") {
17+
x.p; // Error, type any narrowed by primitive type check
18+
~
19+
!!! error TS2339: Property 'p' does not exist on type 'string'.
20+
}
21+
else {
22+
x.p; // No error, type unaffected in this branch
23+
}
24+
25+
if (typeof x === "number") {
26+
x.p; // Error, type any narrowed by primitive type check
27+
~
28+
!!! error TS2339: Property 'p' does not exist on type 'number'.
29+
}
30+
else {
31+
x.p; // No error, type unaffected in this branch
32+
}
33+
34+
if (typeof x === "boolean") {
35+
x.p; // Error, type any narrowed by primitive type check
36+
~
37+
!!! error TS2339: Property 'p' does not exist on type 'boolean'.
38+
}
39+
else {
40+
x.p; // No error, type unaffected in this branch
41+
}
42+
43+
if (typeof x === "object") {
44+
x.p; // No error, type any only affected by primitive type check
45+
}
46+
else {
47+
x.p; // No error, type unaffected in this branch
48+
}
49+
Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,71 @@
11
//// [typeGuardsWithAny.ts]
22
var x: any = { p: 0 };
3+
34
if (x instanceof Object) {
4-
x.p; // No error, type any unaffected by type guard
5+
x.p; // No error, type any unaffected by instanceof type guard
56
}
67
else {
7-
x.p; // No error, type any unaffected by type guard
8+
x.p; // No error, type any unaffected by instanceof type guard
9+
}
10+
11+
if (typeof x === "string") {
12+
x.p; // Error, type any narrowed by primitive type check
13+
}
14+
else {
15+
x.p; // No error, type unaffected in this branch
16+
}
17+
18+
if (typeof x === "number") {
19+
x.p; // Error, type any narrowed by primitive type check
20+
}
21+
else {
22+
x.p; // No error, type unaffected in this branch
23+
}
24+
25+
if (typeof x === "boolean") {
26+
x.p; // Error, type any narrowed by primitive type check
27+
}
28+
else {
29+
x.p; // No error, type unaffected in this branch
30+
}
31+
32+
if (typeof x === "object") {
33+
x.p; // No error, type any only affected by primitive type check
34+
}
35+
else {
36+
x.p; // No error, type unaffected in this branch
837
}
938

1039

1140
//// [typeGuardsWithAny.js]
1241
var x = { p: 0 };
1342
if (x instanceof Object) {
14-
x.p; // No error, type any unaffected by type guard
43+
x.p; // No error, type any unaffected by instanceof type guard
44+
}
45+
else {
46+
x.p; // No error, type any unaffected by instanceof type guard
47+
}
48+
if (typeof x === "string") {
49+
x.p; // Error, type any narrowed by primitive type check
50+
}
51+
else {
52+
x.p; // No error, type unaffected in this branch
53+
}
54+
if (typeof x === "number") {
55+
x.p; // Error, type any narrowed by primitive type check
56+
}
57+
else {
58+
x.p; // No error, type unaffected in this branch
59+
}
60+
if (typeof x === "boolean") {
61+
x.p; // Error, type any narrowed by primitive type check
62+
}
63+
else {
64+
x.p; // No error, type unaffected in this branch
65+
}
66+
if (typeof x === "object") {
67+
x.p; // No error, type any only affected by primitive type check
1568
}
1669
else {
17-
x.p; // No error, type any unaffected by type guard
70+
x.p; // No error, type unaffected in this branch
1871
}

tests/baselines/reference/typeGuardsWithAny.types

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
11
var x: any = { p: 0 };
2+
23
if (x instanceof Object) {
3-
x.p; // No error, type any unaffected by type guard
4+
x.p; // No error, type any unaffected by instanceof type guard
45
}
56
else {
6-
x.p; // No error, type any unaffected by type guard
7+
x.p; // No error, type any unaffected by instanceof type guard
8+
}
9+
10+
if (typeof x === "string") {
11+
x.p; // Error, type any narrowed by primitive type check
12+
}
13+
else {
14+
x.p; // No error, type unaffected in this branch
15+
}
16+
17+
if (typeof x === "number") {
18+
x.p; // Error, type any narrowed by primitive type check
19+
}
20+
else {
21+
x.p; // No error, type unaffected in this branch
22+
}
23+
24+
if (typeof x === "boolean") {
25+
x.p; // Error, type any narrowed by primitive type check
26+
}
27+
else {
28+
x.p; // No error, type unaffected in this branch
29+
}
30+
31+
if (typeof x === "object") {
32+
x.p; // No error, type any only affected by primitive type check
33+
}
34+
else {
35+
x.p; // No error, type unaffected in this branch
736
}

0 commit comments

Comments
 (0)