Skip to content

Commit c923023

Browse files
authored
Merge pull request microsoft#41191 from weswigham/control-flow-comma-exprs
Track control flow for comma expressions in call expressions
2 parents aaa8b74 + 6dde162 commit c923023

File tree

6 files changed

+95
-1
lines changed

6 files changed

+95
-1
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,7 @@ namespace ts {
859859
function isNarrowableReference(expr: Expression): boolean {
860860
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PrivateIdentifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
861861
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
862+
isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) ||
862863
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
863864
isAssignmentExpression(expr) && isNarrowableReference(expr.left);
864865
}

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20777,7 +20777,8 @@ namespace ts {
2077720777
case SyntaxKind.NonNullExpression:
2077820778
return isMatchingReference(source, (target as NonNullExpression | ParenthesizedExpression).expression);
2077920779
case SyntaxKind.BinaryExpression:
20780-
return isAssignmentExpression(target) && isMatchingReference(source, target.left);
20780+
return (isAssignmentExpression(target) && isMatchingReference(source, target.left)) ||
20781+
(isBinaryExpression(target) && target.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source, target.right));
2078120782
}
2078220783
switch (source.kind) {
2078320784
case SyntaxKind.Identifier:
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [controlFlowCommaExpressionFunctionCall.ts]
2+
const otherValue = () => true;
3+
const value : number | string = null as any;
4+
5+
function isNumber(obj: any): obj is number {
6+
return true; // method implementation irrelevant
7+
}
8+
9+
// Bad case - fails
10+
if (isNumber((otherValue(), value))) {
11+
const b = value; // string | number , but should be number
12+
}
13+
14+
//// [controlFlowCommaExpressionFunctionCall.js]
15+
var otherValue = function () { return true; };
16+
var value = null;
17+
function isNumber(obj) {
18+
return true; // method implementation irrelevant
19+
}
20+
// Bad case - fails
21+
if (isNumber((otherValue(), value))) {
22+
var b = value; // string | number , but should be number
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/controlFlowCommaExpressionFunctionCall.ts ===
2+
const otherValue = () => true;
3+
>otherValue : Symbol(otherValue, Decl(controlFlowCommaExpressionFunctionCall.ts, 0, 5))
4+
5+
const value : number | string = null as any;
6+
>value : Symbol(value, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 5))
7+
8+
function isNumber(obj: any): obj is number {
9+
>isNumber : Symbol(isNumber, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 44))
10+
>obj : Symbol(obj, Decl(controlFlowCommaExpressionFunctionCall.ts, 3, 18))
11+
>obj : Symbol(obj, Decl(controlFlowCommaExpressionFunctionCall.ts, 3, 18))
12+
13+
return true; // method implementation irrelevant
14+
}
15+
16+
// Bad case - fails
17+
if (isNumber((otherValue(), value))) {
18+
>isNumber : Symbol(isNumber, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 44))
19+
>otherValue : Symbol(otherValue, Decl(controlFlowCommaExpressionFunctionCall.ts, 0, 5))
20+
>value : Symbol(value, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 5))
21+
22+
const b = value; // string | number , but should be number
23+
>b : Symbol(b, Decl(controlFlowCommaExpressionFunctionCall.ts, 9, 9))
24+
>value : Symbol(value, Decl(controlFlowCommaExpressionFunctionCall.ts, 1, 5))
25+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
=== tests/cases/compiler/controlFlowCommaExpressionFunctionCall.ts ===
2+
const otherValue = () => true;
3+
>otherValue : () => boolean
4+
>() => true : () => boolean
5+
>true : true
6+
7+
const value : number | string = null as any;
8+
>value : string | number
9+
>null as any : any
10+
>null : null
11+
12+
function isNumber(obj: any): obj is number {
13+
>isNumber : (obj: any) => obj is number
14+
>obj : any
15+
16+
return true; // method implementation irrelevant
17+
>true : true
18+
}
19+
20+
// Bad case - fails
21+
if (isNumber((otherValue(), value))) {
22+
>isNumber((otherValue(), value)) : boolean
23+
>isNumber : (obj: any) => obj is number
24+
>(otherValue(), value) : string | number
25+
>otherValue(), value : string | number
26+
>otherValue() : boolean
27+
>otherValue : () => boolean
28+
>value : string | number
29+
30+
const b = value; // string | number , but should be number
31+
>b : number
32+
>value : number
33+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const otherValue = () => true;
2+
const value : number | string = null as any;
3+
4+
function isNumber(obj: any): obj is number {
5+
return true; // method implementation irrelevant
6+
}
7+
8+
// Bad case - fails
9+
if (isNumber((otherValue(), value))) {
10+
const b = value; // string | number , but should be number
11+
}

0 commit comments

Comments
 (0)