Skip to content

Commit 6369d89

Browse files
authored
fix(41492): make more friendly handling the missing call function in binary expressions (#41502)
1 parent 79ffd03 commit 6369d89

6 files changed

+365
-91
lines changed

Diff for: src/compiler/checker.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -34029,7 +34029,16 @@ namespace ts {
3402934029

3403034030
function isFunctionUsedInBinaryExpressionChain(node: Node, testedSymbol: Symbol): boolean {
3403134031
while (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
34032-
if (isCallExpression(node.right) && testedSymbol === getSymbolAtLocation(node.right.expression)) {
34032+
const isUsed = forEachChild(node.right, function visit(child): boolean | undefined {
34033+
if (isIdentifier(child)) {
34034+
const symbol = getSymbolAtLocation(child);
34035+
if (symbol && symbol === testedSymbol) {
34036+
return true;
34037+
}
34038+
}
34039+
return forEachChild(child, visit);
34040+
});
34041+
if (isUsed) {
3403334042
return true;
3403434043
}
3403534044
node = node.parent;

Diff for: tests/baselines/reference/truthinessCallExpressionCoercion2.errors.txt

+37-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(3,5): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
2-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(6,10): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
3-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(30,18): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
4-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(36,46): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
5-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(47,5): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
6-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(50,10): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
7-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(66,9): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
8-
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(69,14): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
1+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(11,5): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
2+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(14,10): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
3+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(41,18): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
4+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(47,46): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
5+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(58,5): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
6+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(61,10): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
7+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(81,5): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
8+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(91,9): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
9+
tests/cases/compiler/truthinessCallExpressionCoercion2.ts(94,14): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
910

1011

11-
==== tests/cases/compiler/truthinessCallExpressionCoercion2.ts (8 errors) ====
12+
==== tests/cases/compiler/truthinessCallExpressionCoercion2.ts (9 errors) ====
13+
declare class A {
14+
static from(): string;
15+
}
16+
17+
declare class B {
18+
static from(): string;
19+
}
20+
1221
function test(required1: () => boolean, required2: () => boolean, optional?: () => boolean) {
1322
// error
1423
required1 && console.log('required');
@@ -41,6 +50,9 @@ tests/cases/compiler/truthinessCallExpressionCoercion2.ts(69,14): error TS2774:
4150
// ok
4251
required1 && required2 && required1() && required2();
4352

53+
// ok
54+
[].forEach((f: () => void) => f && f.apply(parent, []));
55+
4456
// error
4557
required1 && required2 && required1() && console.log('foo');
4658
~~~~~~~~~
@@ -77,6 +89,22 @@ tests/cases/compiler/truthinessCallExpressionCoercion2.ts(69,14): error TS2774:
7789

7890
// ok
7991
x.foo.bar && 1 && x.foo.bar();
92+
93+
// ok
94+
const y = A.from && (A.from as Function) !== B.from ? true : false;
95+
y;
96+
97+
const x1 = {
98+
a: { b: { c: () => {} } }
99+
}
100+
const x2 = {
101+
a: { b: { c: () => {} } }
102+
}
103+
104+
// error
105+
x1.a.b.c && x2.a.b.c();
106+
~~~~~~~~
107+
!!! error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
80108
}
81109

82110
class Foo {

Diff for: tests/baselines/reference/truthinessCallExpressionCoercion2.js

+38
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
//// [truthinessCallExpressionCoercion2.ts]
2+
declare class A {
3+
static from(): string;
4+
}
5+
6+
declare class B {
7+
static from(): string;
8+
}
9+
210
function test(required1: () => boolean, required2: () => boolean, optional?: () => boolean) {
311
// error
412
required1 && console.log('required');
@@ -27,6 +35,9 @@ function test(required1: () => boolean, required2: () => boolean, optional?: ()
2735
// ok
2836
required1 && required2 && required1() && required2();
2937

38+
// ok
39+
[].forEach((f: () => void) => f && f.apply(parent, []));
40+
3041
// error
3142
required1 && required2 && required1() && console.log('foo');
3243
}
@@ -55,6 +66,20 @@ function checksPropertyAccess() {
5566

5667
// ok
5768
x.foo.bar && 1 && x.foo.bar();
69+
70+
// ok
71+
const y = A.from && (A.from as Function) !== B.from ? true : false;
72+
y;
73+
74+
const x1 = {
75+
a: { b: { c: () => {} } }
76+
}
77+
const x2 = {
78+
a: { b: { c: () => {} } }
79+
}
80+
81+
// error
82+
x1.a.b.c && x2.a.b.c();
5883
}
5984

6085
class Foo {
@@ -101,6 +126,8 @@ function test(required1, required2, optional) {
101126
required1() && console.log('required call');
102127
// ok
103128
required1 && required2 && required1() && required2();
129+
// ok
130+
[].forEach(function (f) { return f && f.apply(parent, []); });
104131
// error
105132
required1 && required2 && required1() && console.log('foo');
106133
}
@@ -123,6 +150,17 @@ function checksPropertyAccess() {
123150
x.foo.bar && x.foo.bar();
124151
// ok
125152
x.foo.bar && 1 && x.foo.bar();
153+
// ok
154+
var y = A.from && A.from !== B.from ? true : false;
155+
y;
156+
var x1 = {
157+
a: { b: { c: function () { } } }
158+
};
159+
var x2 = {
160+
a: { b: { c: function () { } } }
161+
};
162+
// error
163+
x1.a.b.c && x2.a.b.c();
126164
}
127165
var Foo = /** @class */ (function () {
128166
function Foo() {

0 commit comments

Comments
 (0)