diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 38c193758be7a..08c6f9732f555 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -35699,15 +35699,14 @@ namespace ts { if (getFalsyFlags(type)) return; const location = isBinaryExpression(condExpr) ? condExpr.right : condExpr; + if (isPropertyAccessExpression(location) && isAssertionExpression(skipParentheses(location.expression))) { + return; + } + const testedNode = isIdentifier(location) ? location : isPropertyAccessExpression(location) ? location.name : isBinaryExpression(location) && isIdentifier(location.right) ? location.right : undefined; - const isPropertyExpressionCast = isPropertyAccessExpression(location) - && isAssertionExpression(skipParentheses(location.expression)); - if (!testedNode || isPropertyExpressionCast) { - return; - } // While it technically should be invalid for any known-truthy value // to be tested, we de-scope to functions and Promises unreferenced in @@ -35720,13 +35719,13 @@ namespace ts { return; } - const testedSymbol = getSymbolAtLocation(testedNode); - if (!testedSymbol) { + const testedSymbol = testedNode && getSymbolAtLocation(testedNode); + if (!testedSymbol && !isPromise) { return; } - const isUsed = isBinaryExpression(condExpr.parent) && isSymbolUsedInBinaryExpressionChain(condExpr.parent, testedSymbol) - || body && isSymbolUsedInConditionBody(condExpr, body, testedNode, testedSymbol); + const isUsed = testedSymbol && isBinaryExpression(condExpr.parent) && isSymbolUsedInBinaryExpressionChain(condExpr.parent, testedSymbol) + || testedSymbol && body && isSymbolUsedInConditionBody(condExpr, body, testedNode!, testedSymbol); if (!isUsed) { if (isPromise) { errorAndMaybeSuggestAwait( diff --git a/tests/baselines/reference/truthinessPromiseCoercion.errors.txt b/tests/baselines/reference/truthinessPromiseCoercion.errors.txt index cacbacfe4ff40..b0c52a2cab78a 100644 --- a/tests/baselines/reference/truthinessPromiseCoercion.errors.txt +++ b/tests/baselines/reference/truthinessPromiseCoercion.errors.txt @@ -1,25 +1,28 @@ -tests/cases/compiler/truthinessPromiseCoercion.ts(6,9): error TS2801: This condition will always return true since this 'Promise' is always defined. -tests/cases/compiler/truthinessPromiseCoercion.ts(10,5): error TS2801: This condition will always return true since this 'Promise' is always defined. -tests/cases/compiler/truthinessPromiseCoercion.ts(31,9): error TS2801: This condition will always return true since this 'Promise' is always defined. +tests/cases/compiler/truthinessPromiseCoercion.ts(7,9): error TS2801: This condition will always return true since this 'Promise' is always defined. +tests/cases/compiler/truthinessPromiseCoercion.ts(11,5): error TS2801: This condition will always return true since this 'Promise' is always defined. +tests/cases/compiler/truthinessPromiseCoercion.ts(32,9): error TS2801: This condition will always return true since this 'Promise' is always defined. +tests/cases/compiler/truthinessPromiseCoercion.ts(40,9): error TS2801: This condition will always return true since this 'Promise' is always defined. +tests/cases/compiler/truthinessPromiseCoercion.ts(43,9): error TS2801: This condition will always return true since this 'Promise' is always defined. -==== tests/cases/compiler/truthinessPromiseCoercion.ts (3 errors) ==== +==== tests/cases/compiler/truthinessPromiseCoercion.ts (5 errors) ==== declare const p: Promise declare const p2: null | Promise declare const obj: { p: Promise } + declare function pf(): Promise async function f() { if (p) {} // err ~ !!! error TS2801: This condition will always return true since this 'Promise' is always defined. -!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:6:9: Did you forget to use 'await'? +!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:7:9: Did you forget to use 'await'? if (!!p) {} // no err if (p2) {} // no err p ? f.arguments : f.arguments; ~ !!! error TS2801: This condition will always return true since this 'Promise' is always defined. -!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:10:5: Did you forget to use 'await'? +!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:11:5: Did you forget to use 'await'? !!p ? f.arguments : f.arguments; p2 ? f.arguments : f.arguments; } @@ -43,10 +46,26 @@ tests/cases/compiler/truthinessPromiseCoercion.ts(31,9): error TS2801: This cond if (obj.p) {} // error ~~~~~ !!! error TS2801: This condition will always return true since this 'Promise' is always defined. -!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:31:9: Did you forget to use 'await'? +!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:32:9: Did you forget to use 'await'? if (obj.p) { // ok await obj.p; } if (obj.p && await obj.p) {} // ok } + + async function i(): Promise { + if (pf()) { // error + ~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:40:9: Did you forget to use 'await'? + return "true"; + } + if (pf()) { // error + ~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 tests/cases/compiler/truthinessPromiseCoercion.ts:43:9: Did you forget to use 'await'? + pf().then(); + } + return "false"; + } \ No newline at end of file diff --git a/tests/baselines/reference/truthinessPromiseCoercion.js b/tests/baselines/reference/truthinessPromiseCoercion.js index 880adbd90cb47..154de6099f11b 100644 --- a/tests/baselines/reference/truthinessPromiseCoercion.js +++ b/tests/baselines/reference/truthinessPromiseCoercion.js @@ -2,6 +2,7 @@ declare const p: Promise declare const p2: null | Promise declare const obj: { p: Promise } +declare function pf(): Promise async function f() { if (p) {} // err @@ -35,6 +36,16 @@ async function h() { } if (obj.p && await obj.p) {} // ok } + +async function i(): Promise { + if (pf()) { // error + return "true"; + } + if (pf()) { // error + pf().then(); + } + return "false"; +} //// [truthinessPromiseCoercion.js] @@ -67,3 +78,12 @@ async function h() { } if (obj.p && await obj.p) { } // ok } +async function i() { + if (pf()) { // error + return "true"; + } + if (pf()) { // error + pf().then(); + } + return "false"; +} diff --git a/tests/baselines/reference/truthinessPromiseCoercion.symbols b/tests/baselines/reference/truthinessPromiseCoercion.symbols index 53301a2c856d6..f025113b86830 100644 --- a/tests/baselines/reference/truthinessPromiseCoercion.symbols +++ b/tests/baselines/reference/truthinessPromiseCoercion.symbols @@ -12,8 +12,12 @@ declare const obj: { p: Promise } >p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 2, 20)) >Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +declare function pf(): Promise +>pf : Symbol(pf, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + async function f() { ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) if (p) {} // err >p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 0, 13)) @@ -27,34 +31,34 @@ async function f() { p ? f.arguments : f.arguments; >p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 0, 13)) >f.arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) >arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) >f.arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) >arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) !!p ? f.arguments : f.arguments; >p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 0, 13)) >f.arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) >arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) >f.arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) >arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) p2 ? f.arguments : f.arguments; >p2 : Symbol(p2, Decl(truthinessPromiseCoercion.ts, 1, 13)) >f.arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) >arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) >f.arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) ->f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>f : Symbol(f, Decl(truthinessPromiseCoercion.ts, 3, 39)) >arguments : Symbol(Function.arguments, Decl(lib.es5.d.ts, --, --)) } // all ok async function g() { ->g : Symbol(g, Decl(truthinessPromiseCoercion.ts, 12, 1)) +>g : Symbol(g, Decl(truthinessPromiseCoercion.ts, 13, 1)) if (p) { >p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 0, 13)) @@ -87,7 +91,7 @@ async function g() { } async function h() { ->h : Symbol(h, Decl(truthinessPromiseCoercion.ts, 27, 1)) +>h : Symbol(h, Decl(truthinessPromiseCoercion.ts, 28, 1)) if (obj.p) {} // error >obj.p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 2, 20)) @@ -113,3 +117,23 @@ async function h() { >p : Symbol(p, Decl(truthinessPromiseCoercion.ts, 2, 20)) } +async function i(): Promise { +>i : Symbol(i, Decl(truthinessPromiseCoercion.ts, 36, 1)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + + if (pf()) { // error +>pf : Symbol(pf, Decl(truthinessPromiseCoercion.ts, 2, 42)) + + return "true"; + } + if (pf()) { // error +>pf : Symbol(pf, Decl(truthinessPromiseCoercion.ts, 2, 42)) + + pf().then(); +>pf().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>pf : Symbol(pf, Decl(truthinessPromiseCoercion.ts, 2, 42)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) + } + return "false"; +} + diff --git a/tests/baselines/reference/truthinessPromiseCoercion.types b/tests/baselines/reference/truthinessPromiseCoercion.types index 3cc81c116b6ef..108171c5766b8 100644 --- a/tests/baselines/reference/truthinessPromiseCoercion.types +++ b/tests/baselines/reference/truthinessPromiseCoercion.types @@ -10,6 +10,9 @@ declare const obj: { p: Promise } >obj : { p: Promise; } >p : Promise +declare function pf(): Promise +>pf : () => Promise + async function f() { >f : () => Promise @@ -132,3 +135,28 @@ async function h() { >p : Promise } +async function i(): Promise { +>i : () => Promise + + if (pf()) { // error +>pf() : Promise +>pf : () => Promise + + return "true"; +>"true" : "true" + } + if (pf()) { // error +>pf() : Promise +>pf : () => Promise + + pf().then(); +>pf().then() : Promise +>pf().then : (onfulfilled?: ((value: boolean) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>pf() : Promise +>pf : () => Promise +>then : (onfulfilled?: ((value: boolean) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise + } + return "false"; +>"false" : "false" +} + diff --git a/tests/cases/compiler/truthinessPromiseCoercion.ts b/tests/cases/compiler/truthinessPromiseCoercion.ts index 03feda55f20a8..ff8f95223d87a 100644 --- a/tests/cases/compiler/truthinessPromiseCoercion.ts +++ b/tests/cases/compiler/truthinessPromiseCoercion.ts @@ -4,6 +4,7 @@ declare const p: Promise declare const p2: null | Promise declare const obj: { p: Promise } +declare function pf(): Promise async function f() { if (p) {} // err @@ -37,3 +38,13 @@ async function h() { } if (obj.p && await obj.p) {} // ok } + +async function i(): Promise { + if (pf()) { // error + return "true"; + } + if (pf()) { // error + pf().then(); + } + return "false"; +}