diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bf1ed0b1206e4..97ac71d9d65b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -44536,31 +44536,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkSourceElement(node.elseStatement); } - function checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(condExpr: Expression, condType: Type, body?: Statement | Expression) { + function checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(inputCondExpr: Expression, condType: Type, body?: Statement | Expression) { if (!strictNullChecks) return; - bothHelper(condExpr, body); - function bothHelper(condExpr: Expression, body: Expression | Statement | undefined) { - condExpr = skipParentheses(condExpr); - - helper(condExpr, body); + bothHelper(inputCondExpr); + function bothHelper(condExpr: Expression) { + condExpr = skipParentheses(condExpr); + helper(condExpr); while (isBinaryExpression(condExpr) && (condExpr.operatorToken.kind === SyntaxKind.BarBarToken || condExpr.operatorToken.kind === SyntaxKind.QuestionQuestionToken)) { condExpr = skipParentheses(condExpr.left); - helper(condExpr, body); + helper(condExpr); } } - function helper(condExpr: Expression, body: Expression | Statement | undefined) { - const location = isLogicalOrCoalescingBinaryExpression(condExpr) ? skipParentheses(condExpr.right) : condExpr; + function helper(condExpr: Expression) { + let inverted = false; + let location = condExpr; + if (isPrefixUnaryExpression(condExpr) && condExpr.operator === SyntaxKind.ExclamationToken) { + location = skipParentheses(condExpr.operand); + inverted = true; + } + location = isLogicalOrCoalescingBinaryExpression(location) ? skipParentheses(location.right) : location; if (isModuleExportsAccessExpression(location)) { return; } if (isLogicalOrCoalescingBinaryExpression(location)) { - bothHelper(location, body); + bothHelper(location); return; } - const type = location === condExpr ? condType : checkExpression(location); + const type = location === inputCondExpr ? condType : checkExpression(location); if (type.flags & TypeFlags.EnumLiteral && isPropertyAccessExpression(location) && (getNodeLinks(location.expression).resolvedSymbol ?? unknownSymbol).flags & SymbolFlags.Enum) { // EnumLiteral type at condition with known value is always truthy or always falsy, likely an error error(location, Diagnostics.This_condition_will_always_return_0, !!(type as LiteralType).value ? "true" : "false"); @@ -44575,8 +44580,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // are too many false positives for values sourced from type // definitions without strictNullChecks otherwise. const callSignatures = getSignaturesOfType(type, SignatureKind.Call); + const isFnCall = callSignatures.length > 0; const isPromise = !!getAwaitedTypeOfPromise(type); - if (callSignatures.length === 0 && !isPromise) { + if (!isFnCall && !isPromise) { return; } @@ -44588,8 +44594,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return; } - const isUsed = testedSymbol && isBinaryExpression(condExpr.parent) && isSymbolUsedInBinaryExpressionChain(condExpr.parent, testedSymbol) - || testedSymbol && body && isSymbolUsedInConditionBody(condExpr, body, testedNode, testedSymbol); + let isUsed: boolean; + if (inverted) { + const closestBlock = findAncestor(condExpr.parent, isBlock); + const isUsedLater = !!(testedSymbol && closestBlock && isSymbolUsedInBody(location, closestBlock, testedNode, testedSymbol, condExpr.end + 1)); + isUsed = isUsedLater; + } + else { + const isUsedInBody = !!(testedSymbol && body && isSymbolUsedInBody(location, body, testedNode, testedSymbol, 0)); + isUsed = isUsedInBody || !!(testedSymbol && isBinaryExpression(condExpr.parent) && isSymbolUsedInBinaryExpressionChain(condExpr.parent, testedSymbol)); + } + if (!isUsed) { if (isPromise) { errorAndMaybeSuggestAwait( @@ -44606,15 +44621,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function isSymbolUsedInConditionBody(expr: Expression, body: Statement | Expression, testedNode: Node, testedSymbol: Symbol): boolean { + function isSymbolUsedInBody(expr: Expression, body: Statement | Expression | Node, testedNode: Node, testedSymbol: Symbol, startPos: number): boolean { return !!forEachChild(body, function check(childNode): boolean | undefined { - if (isIdentifier(childNode)) { + if (childNode.pos >= startPos && isIdentifier(childNode)) { const childSymbol = getSymbolAtLocation(childNode); if (childSymbol && childSymbol === testedSymbol) { // If the test was a simple identifier, the above check is sufficient if (isIdentifier(expr) || isIdentifier(testedNode) && isBinaryExpression(testedNode.parent)) { return true; } + // Otherwise we need to ensure the symbol is called on the same target let testedExpression = testedNode.parent; let childExpression = childNode.parent; diff --git a/tests/baselines/reference/falsinessCallExpressionCoercion.errors.txt b/tests/baselines/reference/falsinessCallExpressionCoercion.errors.txt new file mode 100644 index 0000000000000..e1b08cedb9a7d --- /dev/null +++ b/tests/baselines/reference/falsinessCallExpressionCoercion.errors.txt @@ -0,0 +1,140 @@ +falsinessCallExpressionCoercion.ts(4,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +falsinessCallExpressionCoercion.ts(25,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +falsinessCallExpressionCoercion.ts(41,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +falsinessCallExpressionCoercion.ts(66,14): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +falsinessCallExpressionCoercion.ts(91,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +falsinessCallExpressionCoercion.ts(107,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + + +==== falsinessCallExpressionCoercion.ts (6 errors) ==== + function test1() { + function canAccess() { return false; } + + if (!canAccess) { // error + ~~~~~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + } + } + + function test2() { + function canAccess() { return false; } + + if (!canAccess) { // ok + } + + canAccess(); + } + + function test3() { + function canAccess() { return false; } + + if (!!!canAccess) { // ok + } + } + + function test4(canAccess: () => boolean) { + if (!canAccess) { // error + ~~~~~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + } + } + + function test5(canAccess?: () => boolean) { + if (!canAccess) { // ok + } + } + + function test6() { + const x = { + foo: { + bar() { return true; } + } + }; + + if (!x.foo.bar) { // error + ~~~~~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + } + } + + function test7() { + const x = { + foo: { + bar() { return true; } + } + }; + + if (!x.foo.bar) { // ok + } + + x.foo.bar(); + } + + class Test8 { + maybeIsUser?: () => boolean; + + isUser() { + return true; + } + + test() { + if (!this.isUser) { // error + ~~~~~~~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + } + + if (!this.maybeIsUser) { // ok + } + } + } + + class Test9 { + isUser() { + return true; + } + + test() { + if (!this.isUser) { // ok + } + + this.isUser(); + } + } + + function test10() { + function canAccess() { return false; } + + const res = canAccess + if (!res) { // error + ~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + } + } + + function test11() { + function canAccess() { return false; } + + if (!canAccess) { // ok + } else { + canAccess() + } + } + + function test12() { + function canAccess() { return false; } + + if (!canAccess || Math.random()) { // error + ~~~~~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + } + } + + function test13() { + function canAccess() { return false; } + + if (!canAccess || Math.random()) { // ok + } + + canAccess() + } + \ No newline at end of file diff --git a/tests/baselines/reference/falsinessCallExpressionCoercion.js b/tests/baselines/reference/falsinessCallExpressionCoercion.js new file mode 100644 index 0000000000000..d72eb4d26a7cd --- /dev/null +++ b/tests/baselines/reference/falsinessCallExpressionCoercion.js @@ -0,0 +1,219 @@ +//// [tests/cases/compiler/falsinessCallExpressionCoercion.ts] //// + +//// [falsinessCallExpressionCoercion.ts] +function test1() { + function canAccess() { return false; } + + if (!canAccess) { // error + } +} + +function test2() { + function canAccess() { return false; } + + if (!canAccess) { // ok + } + + canAccess(); +} + +function test3() { + function canAccess() { return false; } + + if (!!!canAccess) { // ok + } +} + +function test4(canAccess: () => boolean) { + if (!canAccess) { // error + } +} + +function test5(canAccess?: () => boolean) { + if (!canAccess) { // ok + } +} + +function test6() { + const x = { + foo: { + bar() { return true; } + } + }; + + if (!x.foo.bar) { // error + } +} + +function test7() { + const x = { + foo: { + bar() { return true; } + } + }; + + if (!x.foo.bar) { // ok + } + + x.foo.bar(); +} + +class Test8 { + maybeIsUser?: () => boolean; + + isUser() { + return true; + } + + test() { + if (!this.isUser) { // error + } + + if (!this.maybeIsUser) { // ok + } + } +} + +class Test9 { + isUser() { + return true; + } + + test() { + if (!this.isUser) { // ok + } + + this.isUser(); + } +} + +function test10() { + function canAccess() { return false; } + + const res = canAccess + if (!res) { // error + } +} + +function test11() { + function canAccess() { return false; } + + if (!canAccess) { // ok + } else { + canAccess() + } +} + +function test12() { + function canAccess() { return false; } + + if (!canAccess || Math.random()) { // error + } +} + +function test13() { + function canAccess() { return false; } + + if (!canAccess || Math.random()) { // ok + } + + canAccess() +} + + +//// [falsinessCallExpressionCoercion.js] +function test1() { + function canAccess() { return false; } + if (!canAccess) { // error + } +} +function test2() { + function canAccess() { return false; } + if (!canAccess) { // ok + } + canAccess(); +} +function test3() { + function canAccess() { return false; } + if (!!!canAccess) { // ok + } +} +function test4(canAccess) { + if (!canAccess) { // error + } +} +function test5(canAccess) { + if (!canAccess) { // ok + } +} +function test6() { + var x = { + foo: { + bar: function () { return true; } + } + }; + if (!x.foo.bar) { // error + } +} +function test7() { + var x = { + foo: { + bar: function () { return true; } + } + }; + if (!x.foo.bar) { // ok + } + x.foo.bar(); +} +var Test8 = /** @class */ (function () { + function Test8() { + } + Test8.prototype.isUser = function () { + return true; + }; + Test8.prototype.test = function () { + if (!this.isUser) { // error + } + if (!this.maybeIsUser) { // ok + } + }; + return Test8; +}()); +var Test9 = /** @class */ (function () { + function Test9() { + } + Test9.prototype.isUser = function () { + return true; + }; + Test9.prototype.test = function () { + if (!this.isUser) { // ok + } + this.isUser(); + }; + return Test9; +}()); +function test10() { + function canAccess() { return false; } + var res = canAccess; + if (!res) { // error + } +} +function test11() { + function canAccess() { return false; } + if (!canAccess) { // ok + } + else { + canAccess(); + } +} +function test12() { + function canAccess() { return false; } + if (!canAccess || Math.random()) { // error + } +} +function test13() { + function canAccess() { return false; } + if (!canAccess || Math.random()) { // ok + } + canAccess(); +} diff --git a/tests/baselines/reference/falsinessCallExpressionCoercion.symbols b/tests/baselines/reference/falsinessCallExpressionCoercion.symbols new file mode 100644 index 0000000000000..c6430ed13cfee --- /dev/null +++ b/tests/baselines/reference/falsinessCallExpressionCoercion.symbols @@ -0,0 +1,225 @@ +//// [tests/cases/compiler/falsinessCallExpressionCoercion.ts] //// + +=== falsinessCallExpressionCoercion.ts === +function test1() { +>test1 : Symbol(test1, Decl(falsinessCallExpressionCoercion.ts, 0, 0)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 0, 18)) + + if (!canAccess) { // error +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 0, 18)) + } +} + +function test2() { +>test2 : Symbol(test2, Decl(falsinessCallExpressionCoercion.ts, 5, 1)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 7, 18)) + + if (!canAccess) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 7, 18)) + } + + canAccess(); +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 7, 18)) +} + +function test3() { +>test3 : Symbol(test3, Decl(falsinessCallExpressionCoercion.ts, 14, 1)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 16, 18)) + + if (!!!canAccess) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 16, 18)) + } +} + +function test4(canAccess: () => boolean) { +>test4 : Symbol(test4, Decl(falsinessCallExpressionCoercion.ts, 21, 1)) +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 23, 15)) + + if (!canAccess) { // error +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 23, 15)) + } +} + +function test5(canAccess?: () => boolean) { +>test5 : Symbol(test5, Decl(falsinessCallExpressionCoercion.ts, 26, 1)) +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 28, 15)) + + if (!canAccess) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 28, 15)) + } +} + +function test6() { +>test6 : Symbol(test6, Decl(falsinessCallExpressionCoercion.ts, 31, 1)) + + const x = { +>x : Symbol(x, Decl(falsinessCallExpressionCoercion.ts, 34, 9)) + + foo: { +>foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 34, 15)) + + bar() { return true; } +>bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 35, 14)) + } + }; + + if (!x.foo.bar) { // error +>x.foo.bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 35, 14)) +>x.foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 34, 15)) +>x : Symbol(x, Decl(falsinessCallExpressionCoercion.ts, 34, 9)) +>foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 34, 15)) +>bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 35, 14)) + } +} + +function test7() { +>test7 : Symbol(test7, Decl(falsinessCallExpressionCoercion.ts, 42, 1)) + + const x = { +>x : Symbol(x, Decl(falsinessCallExpressionCoercion.ts, 45, 9)) + + foo: { +>foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 45, 15)) + + bar() { return true; } +>bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 46, 14)) + } + }; + + if (!x.foo.bar) { // ok +>x.foo.bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 46, 14)) +>x.foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 45, 15)) +>x : Symbol(x, Decl(falsinessCallExpressionCoercion.ts, 45, 9)) +>foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 45, 15)) +>bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 46, 14)) + } + + x.foo.bar(); +>x.foo.bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 46, 14)) +>x.foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 45, 15)) +>x : Symbol(x, Decl(falsinessCallExpressionCoercion.ts, 45, 9)) +>foo : Symbol(foo, Decl(falsinessCallExpressionCoercion.ts, 45, 15)) +>bar : Symbol(bar, Decl(falsinessCallExpressionCoercion.ts, 46, 14)) +} + +class Test8 { +>Test8 : Symbol(Test8, Decl(falsinessCallExpressionCoercion.ts, 55, 1)) + + maybeIsUser?: () => boolean; +>maybeIsUser : Symbol(Test8.maybeIsUser, Decl(falsinessCallExpressionCoercion.ts, 57, 13)) + + isUser() { +>isUser : Symbol(Test8.isUser, Decl(falsinessCallExpressionCoercion.ts, 58, 32)) + + return true; + } + + test() { +>test : Symbol(Test8.test, Decl(falsinessCallExpressionCoercion.ts, 62, 5)) + + if (!this.isUser) { // error +>this.isUser : Symbol(Test8.isUser, Decl(falsinessCallExpressionCoercion.ts, 58, 32)) +>this : Symbol(Test8, Decl(falsinessCallExpressionCoercion.ts, 55, 1)) +>isUser : Symbol(Test8.isUser, Decl(falsinessCallExpressionCoercion.ts, 58, 32)) + } + + if (!this.maybeIsUser) { // ok +>this.maybeIsUser : Symbol(Test8.maybeIsUser, Decl(falsinessCallExpressionCoercion.ts, 57, 13)) +>this : Symbol(Test8, Decl(falsinessCallExpressionCoercion.ts, 55, 1)) +>maybeIsUser : Symbol(Test8.maybeIsUser, Decl(falsinessCallExpressionCoercion.ts, 57, 13)) + } + } +} + +class Test9 { +>Test9 : Symbol(Test9, Decl(falsinessCallExpressionCoercion.ts, 71, 1)) + + isUser() { +>isUser : Symbol(Test9.isUser, Decl(falsinessCallExpressionCoercion.ts, 73, 13)) + + return true; + } + + test() { +>test : Symbol(Test9.test, Decl(falsinessCallExpressionCoercion.ts, 76, 5)) + + if (!this.isUser) { // ok +>this.isUser : Symbol(Test9.isUser, Decl(falsinessCallExpressionCoercion.ts, 73, 13)) +>this : Symbol(Test9, Decl(falsinessCallExpressionCoercion.ts, 71, 1)) +>isUser : Symbol(Test9.isUser, Decl(falsinessCallExpressionCoercion.ts, 73, 13)) + } + + this.isUser(); +>this.isUser : Symbol(Test9.isUser, Decl(falsinessCallExpressionCoercion.ts, 73, 13)) +>this : Symbol(Test9, Decl(falsinessCallExpressionCoercion.ts, 71, 1)) +>isUser : Symbol(Test9.isUser, Decl(falsinessCallExpressionCoercion.ts, 73, 13)) + } +} + +function test10() { +>test10 : Symbol(test10, Decl(falsinessCallExpressionCoercion.ts, 84, 1)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 86, 19)) + + const res = canAccess +>res : Symbol(res, Decl(falsinessCallExpressionCoercion.ts, 89, 9)) +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 86, 19)) + + if (!res) { // error +>res : Symbol(res, Decl(falsinessCallExpressionCoercion.ts, 89, 9)) + } +} + +function test11() { +>test11 : Symbol(test11, Decl(falsinessCallExpressionCoercion.ts, 92, 1)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 94, 19)) + + if (!canAccess) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 94, 19)) + + } else { + canAccess() +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 94, 19)) + } +} + +function test12() { +>test12 : Symbol(test12, Decl(falsinessCallExpressionCoercion.ts, 101, 1)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 103, 19)) + + if (!canAccess || Math.random()) { // error +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 103, 19)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + } +} + +function test13() { +>test13 : Symbol(test13, Decl(falsinessCallExpressionCoercion.ts, 108, 1)) + + function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 110, 19)) + + if (!canAccess || Math.random()) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 110, 19)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + } + + canAccess() +>canAccess : Symbol(canAccess, Decl(falsinessCallExpressionCoercion.ts, 110, 19)) +} + diff --git a/tests/baselines/reference/falsinessCallExpressionCoercion.types b/tests/baselines/reference/falsinessCallExpressionCoercion.types new file mode 100644 index 0000000000000..f9fd32c6c2239 --- /dev/null +++ b/tests/baselines/reference/falsinessCallExpressionCoercion.types @@ -0,0 +1,385 @@ +//// [tests/cases/compiler/falsinessCallExpressionCoercion.ts] //// + +=== falsinessCallExpressionCoercion.ts === +function test1() { +>test1 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!canAccess) { // error +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ + } +} + +function test2() { +>test2 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!canAccess) { // ok +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ + } + + canAccess(); +>canAccess() : boolean +> : ^^^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +} + +function test3() { +>test3 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!!!canAccess) { // ok +>!!!canAccess : false +> : ^^^^^ +>!!canAccess : true +> : ^^^^ +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ + } +} + +function test4(canAccess: () => boolean) { +>test4 : (canAccess: () => boolean) => void +> : ^ ^^ ^^^^^^^^^ +>canAccess : () => boolean +> : ^^^^^^ + + if (!canAccess) { // error +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^ + } +} + +function test5(canAccess?: () => boolean) { +>test5 : (canAccess?: () => boolean) => void +> : ^ ^^^ ^^^^^^^^^ +>canAccess : (() => boolean) | undefined +> : ^^^^^^^ ^^^^^^^^^^^^^ + + if (!canAccess) { // ok +>!canAccess : boolean +> : ^^^^^^^ +>canAccess : (() => boolean) | undefined +> : ^^^^^^^ ^^^^^^^^^^^^^ + } +} + +function test6() { +>test6 : () => void +> : ^^^^^^^^^^ + + const x = { +>x : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ foo: { bar() { return true; } } } : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + foo: { +>foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>{ bar() { return true; } } : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ + + bar() { return true; } +>bar : () => boolean +> : ^^^^^^^^^^^^^ +>true : true +> : ^^^^ + } + }; + + if (!x.foo.bar) { // error +>!x.foo.bar : false +> : ^^^^^ +>x.foo.bar : () => boolean +> : ^^^^^^^^^^^^^ +>x.foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>x : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>bar : () => boolean +> : ^^^^^^^^^^^^^ + } +} + +function test7() { +>test7 : () => void +> : ^^^^^^^^^^ + + const x = { +>x : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ foo: { bar() { return true; } } } : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + foo: { +>foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>{ bar() { return true; } } : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ + + bar() { return true; } +>bar : () => boolean +> : ^^^^^^^^^^^^^ +>true : true +> : ^^^^ + } + }; + + if (!x.foo.bar) { // ok +>!x.foo.bar : false +> : ^^^^^ +>x.foo.bar : () => boolean +> : ^^^^^^^^^^^^^ +>x.foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>x : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>bar : () => boolean +> : ^^^^^^^^^^^^^ + } + + x.foo.bar(); +>x.foo.bar() : boolean +> : ^^^^^^^ +>x.foo.bar : () => boolean +> : ^^^^^^^^^^^^^ +>x.foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>x : { foo: { bar(): boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : { bar(): boolean; } +> : ^^^^^^^^^^^^^^^^^^^ +>bar : () => boolean +> : ^^^^^^^^^^^^^ +} + +class Test8 { +>Test8 : Test8 +> : ^^^^^ + + maybeIsUser?: () => boolean; +>maybeIsUser : (() => boolean) | undefined +> : ^^^^^^^ ^^^^^^^^^^^^^ + + isUser() { +>isUser : () => boolean +> : ^^^^^^^^^^^^^ + + return true; +>true : true +> : ^^^^ + } + + test() { +>test : () => void +> : ^^^^^^^^^^ + + if (!this.isUser) { // error +>!this.isUser : false +> : ^^^^^ +>this.isUser : () => boolean +> : ^^^^^^^^^^^^^ +>this : this +> : ^^^^ +>isUser : () => boolean +> : ^^^^^^^^^^^^^ + } + + if (!this.maybeIsUser) { // ok +>!this.maybeIsUser : boolean +> : ^^^^^^^ +>this.maybeIsUser : (() => boolean) | undefined +> : ^^^^^^^ ^^^^^^^^^^^^^ +>this : this +> : ^^^^ +>maybeIsUser : (() => boolean) | undefined +> : ^^^^^^^ ^^^^^^^^^^^^^ + } + } +} + +class Test9 { +>Test9 : Test9 +> : ^^^^^ + + isUser() { +>isUser : () => boolean +> : ^^^^^^^^^^^^^ + + return true; +>true : true +> : ^^^^ + } + + test() { +>test : () => void +> : ^^^^^^^^^^ + + if (!this.isUser) { // ok +>!this.isUser : false +> : ^^^^^ +>this.isUser : () => boolean +> : ^^^^^^^^^^^^^ +>this : this +> : ^^^^ +>isUser : () => boolean +> : ^^^^^^^^^^^^^ + } + + this.isUser(); +>this.isUser() : boolean +> : ^^^^^^^ +>this.isUser : () => boolean +> : ^^^^^^^^^^^^^ +>this : this +> : ^^^^ +>isUser : () => boolean +> : ^^^^^^^^^^^^^ + } +} + +function test10() { +>test10 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + const res = canAccess +>res : () => boolean +> : ^^^^^^^^^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ + + if (!res) { // error +>!res : false +> : ^^^^^ +>res : () => boolean +> : ^^^^^^^^^^^^^ + } +} + +function test11() { +>test11 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!canAccess) { // ok +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ + + } else { + canAccess() +>canAccess() : boolean +> : ^^^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ + } +} + +function test12() { +>test12 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!canAccess || Math.random()) { // error +>!canAccess || Math.random() : number +> : ^^^^^^ +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + } +} + +function test13() { +>test13 : () => void +> : ^^^^^^^^^^ + + function canAccess() { return false; } +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!canAccess || Math.random()) { // ok +>!canAccess || Math.random() : number +> : ^^^^^^ +>!canAccess : false +> : ^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + } + + canAccess() +>canAccess() : boolean +> : ^^^^^^^ +>canAccess : () => boolean +> : ^^^^^^^^^^^^^ +} + diff --git a/tests/baselines/reference/falsinessPromiseCoercion.errors.txt b/tests/baselines/reference/falsinessPromiseCoercion.errors.txt new file mode 100644 index 0000000000000..8c489579198b4 --- /dev/null +++ b/tests/baselines/reference/falsinessPromiseCoercion.errors.txt @@ -0,0 +1,106 @@ +falsinessPromiseCoercion.ts(4,10): error TS2801: This condition will always return true since this 'Promise' is always defined. +falsinessPromiseCoercion.ts(28,10): error TS2801: This condition will always return true since this 'Promise' is always defined. +falsinessPromiseCoercion.ts(44,10): error TS2801: This condition will always return true since this 'Promise' is always defined. +falsinessPromiseCoercion.ts(69,14): error TS2801: This condition will always return true since this 'Promise' is always defined. + + +==== falsinessPromiseCoercion.ts (4 errors) ==== + function test1() { + async function canAccess() { return false; } + + if (!canAccess()) { // error + ~~~~~~~~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 falsinessPromiseCoercion.ts:4:10: Did you forget to use 'await'? + } + } + + async function test2() { + async function canAccess() { return false; } + + const res = canAccess() + + if (!res) { // ok + return + } + + await res + } + + function test3() { + async function canAccess() { return false; } + + if (!!!canAccess()) { // ok + } + } + + function test4(canAccess: () => Promise) { + if (!canAccess()) { // error + ~~~~~~~~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 falsinessPromiseCoercion.ts:28:10: Did you forget to use 'await'? + } + } + + function test5(canAccess: () => Promise | undefined) { + if (!canAccess()) { // ok + } + } + + function test6() { + const x = { + foo: { + async bar() { return true; } + } + }; + + if (!x.foo.bar()) { // error + ~~~~~~~~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 falsinessPromiseCoercion.ts:44:10: Did you forget to use 'await'? + } + } + + async function test7() { + const x = { + foo: { + async bar() { return true; } + } + }; + + const res = x.foo.bar(); + + if (!res) { // ok + } + + await res; + } + + class Test8 { + async isUser() { + return true; + } + + test() { + if (!this.isUser()) { // error + ~~~~~~~~~~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 falsinessPromiseCoercion.ts:69:14: Did you forget to use 'await'? + } + } + } + + class Test9 { + async isUser() { + return true; + } + + async test() { + const res = this.isUser(); + if (!res) { // ok + } + + await res; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/falsinessPromiseCoercion.js b/tests/baselines/reference/falsinessPromiseCoercion.js new file mode 100644 index 0000000000000..93e67c9f7d496 --- /dev/null +++ b/tests/baselines/reference/falsinessPromiseCoercion.js @@ -0,0 +1,158 @@ +//// [tests/cases/compiler/falsinessPromiseCoercion.ts] //// + +//// [falsinessPromiseCoercion.ts] +function test1() { + async function canAccess() { return false; } + + if (!canAccess()) { // error + } +} + +async function test2() { + async function canAccess() { return false; } + + const res = canAccess() + + if (!res) { // ok + return + } + + await res +} + +function test3() { + async function canAccess() { return false; } + + if (!!!canAccess()) { // ok + } +} + +function test4(canAccess: () => Promise) { + if (!canAccess()) { // error + } +} + +function test5(canAccess: () => Promise | undefined) { + if (!canAccess()) { // ok + } +} + +function test6() { + const x = { + foo: { + async bar() { return true; } + } + }; + + if (!x.foo.bar()) { // error + } +} + +async function test7() { + const x = { + foo: { + async bar() { return true; } + } + }; + + const res = x.foo.bar(); + + if (!res) { // ok + } + + await res; +} + +class Test8 { + async isUser() { + return true; + } + + test() { + if (!this.isUser()) { // error + } + } +} + +class Test9 { + async isUser() { + return true; + } + + async test() { + const res = this.isUser(); + if (!res) { // ok + } + + await res; + } +} + + +//// [falsinessPromiseCoercion.js] +function test1() { + async function canAccess() { return false; } + if (!canAccess()) { // error + } +} +async function test2() { + async function canAccess() { return false; } + const res = canAccess(); + if (!res) { // ok + return; + } + await res; +} +function test3() { + async function canAccess() { return false; } + if (!!!canAccess()) { // ok + } +} +function test4(canAccess) { + if (!canAccess()) { // error + } +} +function test5(canAccess) { + if (!canAccess()) { // ok + } +} +function test6() { + const x = { + foo: { + async bar() { return true; } + } + }; + if (!x.foo.bar()) { // error + } +} +async function test7() { + const x = { + foo: { + async bar() { return true; } + } + }; + const res = x.foo.bar(); + if (!res) { // ok + } + await res; +} +class Test8 { + async isUser() { + return true; + } + test() { + if (!this.isUser()) { // error + } + } +} +class Test9 { + async isUser() { + return true; + } + async test() { + const res = this.isUser(); + if (!res) { // ok + } + await res; + } +} diff --git a/tests/baselines/reference/falsinessPromiseCoercion.symbols b/tests/baselines/reference/falsinessPromiseCoercion.symbols new file mode 100644 index 0000000000000..2ceac9f1d62b0 --- /dev/null +++ b/tests/baselines/reference/falsinessPromiseCoercion.symbols @@ -0,0 +1,165 @@ +//// [tests/cases/compiler/falsinessPromiseCoercion.ts] //// + +=== falsinessPromiseCoercion.ts === +function test1() { +>test1 : Symbol(test1, Decl(falsinessPromiseCoercion.ts, 0, 0)) + + async function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 0, 18)) + + if (!canAccess()) { // error +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 0, 18)) + } +} + +async function test2() { +>test2 : Symbol(test2, Decl(falsinessPromiseCoercion.ts, 5, 1)) + + async function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 7, 24)) + + const res = canAccess() +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 10, 9)) +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 7, 24)) + + if (!res) { // ok +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 10, 9)) + + return + } + + await res +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 10, 9)) +} + +function test3() { +>test3 : Symbol(test3, Decl(falsinessPromiseCoercion.ts, 17, 1)) + + async function canAccess() { return false; } +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 19, 18)) + + if (!!!canAccess()) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 19, 18)) + } +} + +function test4(canAccess: () => Promise) { +>test4 : Symbol(test4, Decl(falsinessPromiseCoercion.ts, 24, 1)) +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 26, 15)) +>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 (!canAccess()) { // error +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 26, 15)) + } +} + +function test5(canAccess: () => Promise | undefined) { +>test5 : Symbol(test5, Decl(falsinessPromiseCoercion.ts, 29, 1)) +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 31, 15)) +>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 (!canAccess()) { // ok +>canAccess : Symbol(canAccess, Decl(falsinessPromiseCoercion.ts, 31, 15)) + } +} + +function test6() { +>test6 : Symbol(test6, Decl(falsinessPromiseCoercion.ts, 34, 1)) + + const x = { +>x : Symbol(x, Decl(falsinessPromiseCoercion.ts, 37, 9)) + + foo: { +>foo : Symbol(foo, Decl(falsinessPromiseCoercion.ts, 37, 15)) + + async bar() { return true; } +>bar : Symbol(bar, Decl(falsinessPromiseCoercion.ts, 38, 14)) + } + }; + + if (!x.foo.bar()) { // error +>x.foo.bar : Symbol(bar, Decl(falsinessPromiseCoercion.ts, 38, 14)) +>x.foo : Symbol(foo, Decl(falsinessPromiseCoercion.ts, 37, 15)) +>x : Symbol(x, Decl(falsinessPromiseCoercion.ts, 37, 9)) +>foo : Symbol(foo, Decl(falsinessPromiseCoercion.ts, 37, 15)) +>bar : Symbol(bar, Decl(falsinessPromiseCoercion.ts, 38, 14)) + } +} + +async function test7() { +>test7 : Symbol(test7, Decl(falsinessPromiseCoercion.ts, 45, 1)) + + const x = { +>x : Symbol(x, Decl(falsinessPromiseCoercion.ts, 48, 9)) + + foo: { +>foo : Symbol(foo, Decl(falsinessPromiseCoercion.ts, 48, 15)) + + async bar() { return true; } +>bar : Symbol(bar, Decl(falsinessPromiseCoercion.ts, 49, 14)) + } + }; + + const res = x.foo.bar(); +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 54, 9)) +>x.foo.bar : Symbol(bar, Decl(falsinessPromiseCoercion.ts, 49, 14)) +>x.foo : Symbol(foo, Decl(falsinessPromiseCoercion.ts, 48, 15)) +>x : Symbol(x, Decl(falsinessPromiseCoercion.ts, 48, 9)) +>foo : Symbol(foo, Decl(falsinessPromiseCoercion.ts, 48, 15)) +>bar : Symbol(bar, Decl(falsinessPromiseCoercion.ts, 49, 14)) + + if (!res) { // ok +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 54, 9)) + } + + await res; +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 54, 9)) +} + +class Test8 { +>Test8 : Symbol(Test8, Decl(falsinessPromiseCoercion.ts, 60, 1)) + + async isUser() { +>isUser : Symbol(Test8.isUser, Decl(falsinessPromiseCoercion.ts, 62, 13)) + + return true; + } + + test() { +>test : Symbol(Test8.test, Decl(falsinessPromiseCoercion.ts, 65, 5)) + + if (!this.isUser()) { // error +>this.isUser : Symbol(Test8.isUser, Decl(falsinessPromiseCoercion.ts, 62, 13)) +>this : Symbol(Test8, Decl(falsinessPromiseCoercion.ts, 60, 1)) +>isUser : Symbol(Test8.isUser, Decl(falsinessPromiseCoercion.ts, 62, 13)) + } + } +} + +class Test9 { +>Test9 : Symbol(Test9, Decl(falsinessPromiseCoercion.ts, 71, 1)) + + async isUser() { +>isUser : Symbol(Test9.isUser, Decl(falsinessPromiseCoercion.ts, 73, 13)) + + return true; + } + + async test() { +>test : Symbol(Test9.test, Decl(falsinessPromiseCoercion.ts, 76, 5)) + + const res = this.isUser(); +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 79, 13)) +>this.isUser : Symbol(Test9.isUser, Decl(falsinessPromiseCoercion.ts, 73, 13)) +>this : Symbol(Test9, Decl(falsinessPromiseCoercion.ts, 71, 1)) +>isUser : Symbol(Test9.isUser, Decl(falsinessPromiseCoercion.ts, 73, 13)) + + if (!res) { // ok +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 79, 13)) + } + + await res; +>res : Symbol(res, Decl(falsinessPromiseCoercion.ts, 79, 13)) + } +} + diff --git a/tests/baselines/reference/falsinessPromiseCoercion.types b/tests/baselines/reference/falsinessPromiseCoercion.types new file mode 100644 index 0000000000000..35e1b522b9487 --- /dev/null +++ b/tests/baselines/reference/falsinessPromiseCoercion.types @@ -0,0 +1,285 @@ +//// [tests/cases/compiler/falsinessPromiseCoercion.ts] //// + +=== falsinessPromiseCoercion.ts === +function test1() { +>test1 : () => void +> : ^^^^^^^^^^ + + async function canAccess() { return false; } +>canAccess : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!canAccess()) { // error +>!canAccess() : false +> : ^^^^^ +>canAccess() : Promise +> : ^^^^^^^^^^^^^^^^ +>canAccess : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + } +} + +async function test2() { +>test2 : () => Promise +> : ^^^^^^^^^^^^^^^^^^^ + + async function canAccess() { return false; } +>canAccess : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + const res = canAccess() +>res : Promise +> : ^^^^^^^^^^^^^^^^ +>canAccess() : Promise +> : ^^^^^^^^^^^^^^^^ +>canAccess : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + + if (!res) { // ok +>!res : false +> : ^^^^^ +>res : Promise +> : ^^^^^^^^^^^^^^^^ + + return + } + + await res +>await res : boolean +> : ^^^^^^^ +>res : Promise +> : ^^^^^^^^^^^^^^^^ +} + +function test3() { +>test3 : () => void +> : ^^^^^^^^^^ + + async function canAccess() { return false; } +>canAccess : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>false : false +> : ^^^^^ + + if (!!!canAccess()) { // ok +>!!!canAccess() : false +> : ^^^^^ +>!!canAccess() : true +> : ^^^^ +>!canAccess() : false +> : ^^^^^ +>canAccess() : Promise +> : ^^^^^^^^^^^^^^^^ +>canAccess : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + } +} + +function test4(canAccess: () => Promise) { +>test4 : (canAccess: () => Promise) => void +> : ^ ^^ ^^^^^^^^^ +>canAccess : () => Promise +> : ^^^^^^ + + if (!canAccess()) { // error +>!canAccess() : false +> : ^^^^^ +>canAccess() : Promise +> : ^^^^^^^^^^^^^^^^ +>canAccess : () => Promise +> : ^^^^^^ + } +} + +function test5(canAccess: () => Promise | undefined) { +>test5 : (canAccess: () => Promise | undefined) => void +> : ^ ^^ ^^^^^^^^^ +>canAccess : () => Promise | undefined +> : ^^^^^^ + + if (!canAccess()) { // ok +>!canAccess() : boolean +> : ^^^^^^^ +>canAccess() : Promise | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>canAccess : () => Promise | undefined +> : ^^^^^^ + } +} + +function test6() { +>test6 : () => void +> : ^^^^^^^^^^ + + const x = { +>x : { foo: { bar(): Promise; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ foo: { async bar() { return true; } } } : { foo: { bar(): Promise; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + foo: { +>foo : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ async bar() { return true; } } : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + async bar() { return true; } +>bar : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>true : true +> : ^^^^ + } + }; + + if (!x.foo.bar()) { // error +>!x.foo.bar() : false +> : ^^^^^ +>x.foo.bar() : Promise +> : ^^^^^^^^^^^^^^^^ +>x.foo.bar : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>x.foo : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x : { foo: { bar(): Promise; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>bar : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + } +} + +async function test7() { +>test7 : () => Promise +> : ^^^^^^^^^^^^^^^^^^^ + + const x = { +>x : { foo: { bar(): Promise; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ foo: { async bar() { return true; } } } : { foo: { bar(): Promise; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + foo: { +>foo : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ async bar() { return true; } } : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + async bar() { return true; } +>bar : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>true : true +> : ^^^^ + } + }; + + const res = x.foo.bar(); +>res : Promise +> : ^^^^^^^^^^^^^^^^ +>x.foo.bar() : Promise +> : ^^^^^^^^^^^^^^^^ +>x.foo.bar : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>x.foo : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x : { foo: { bar(): Promise; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : { bar(): Promise; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>bar : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + + if (!res) { // ok +>!res : false +> : ^^^^^ +>res : Promise +> : ^^^^^^^^^^^^^^^^ + } + + await res; +>await res : boolean +> : ^^^^^^^ +>res : Promise +> : ^^^^^^^^^^^^^^^^ +} + +class Test8 { +>Test8 : Test8 +> : ^^^^^ + + async isUser() { +>isUser : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + + return true; +>true : true +> : ^^^^ + } + + test() { +>test : () => void +> : ^^^^^^^^^^ + + if (!this.isUser()) { // error +>!this.isUser() : false +> : ^^^^^ +>this.isUser() : Promise +> : ^^^^^^^^^^^^^^^^ +>this.isUser : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>this : this +> : ^^^^ +>isUser : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + } + } +} + +class Test9 { +>Test9 : Test9 +> : ^^^^^ + + async isUser() { +>isUser : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + + return true; +>true : true +> : ^^^^ + } + + async test() { +>test : () => Promise +> : ^^^^^^^^^^^^^^^^^^^ + + const res = this.isUser(); +>res : Promise +> : ^^^^^^^^^^^^^^^^ +>this.isUser() : Promise +> : ^^^^^^^^^^^^^^^^ +>this.isUser : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ +>this : this +> : ^^^^ +>isUser : () => Promise +> : ^^^^^^^^^^^^^^^^^^^^^^ + + if (!res) { // ok +>!res : false +> : ^^^^^ +>res : Promise +> : ^^^^^^^^^^^^^^^^ + } + + await res; +>await res : boolean +> : ^^^^^^^ +>res : Promise +> : ^^^^^^^^^^^^^^^^ + } +} + diff --git a/tests/cases/compiler/falsinessCallExpressionCoercion.ts b/tests/cases/compiler/falsinessCallExpressionCoercion.ts new file mode 100644 index 0000000000000..5922f66d8ae5f --- /dev/null +++ b/tests/cases/compiler/falsinessCallExpressionCoercion.ts @@ -0,0 +1,120 @@ +// @strictNullChecks:true + +function test1() { + function canAccess() { return false; } + + if (!canAccess) { // error + } +} + +function test2() { + function canAccess() { return false; } + + if (!canAccess) { // ok + } + + canAccess(); +} + +function test3() { + function canAccess() { return false; } + + if (!!!canAccess) { // ok + } +} + +function test4(canAccess: () => boolean) { + if (!canAccess) { // error + } +} + +function test5(canAccess?: () => boolean) { + if (!canAccess) { // ok + } +} + +function test6() { + const x = { + foo: { + bar() { return true; } + } + }; + + if (!x.foo.bar) { // error + } +} + +function test7() { + const x = { + foo: { + bar() { return true; } + } + }; + + if (!x.foo.bar) { // ok + } + + x.foo.bar(); +} + +class Test8 { + maybeIsUser?: () => boolean; + + isUser() { + return true; + } + + test() { + if (!this.isUser) { // error + } + + if (!this.maybeIsUser) { // ok + } + } +} + +class Test9 { + isUser() { + return true; + } + + test() { + if (!this.isUser) { // ok + } + + this.isUser(); + } +} + +function test10() { + function canAccess() { return false; } + + const res = canAccess + if (!res) { // error + } +} + +function test11() { + function canAccess() { return false; } + + if (!canAccess) { // ok + } else { + canAccess() + } +} + +function test12() { + function canAccess() { return false; } + + if (!canAccess || Math.random()) { // error + } +} + +function test13() { + function canAccess() { return false; } + + if (!canAccess || Math.random()) { // ok + } + + canAccess() +} diff --git a/tests/cases/compiler/falsinessPromiseCoercion.ts b/tests/cases/compiler/falsinessPromiseCoercion.ts new file mode 100644 index 0000000000000..7f37c1a161a31 --- /dev/null +++ b/tests/cases/compiler/falsinessPromiseCoercion.ts @@ -0,0 +1,89 @@ +// @strictNullChecks:true +// @target:esnext + +function test1() { + async function canAccess() { return false; } + + if (!canAccess()) { // error + } +} + +async function test2() { + async function canAccess() { return false; } + + const res = canAccess() + + if (!res) { // ok + return + } + + await res +} + +function test3() { + async function canAccess() { return false; } + + if (!!!canAccess()) { // ok + } +} + +function test4(canAccess: () => Promise) { + if (!canAccess()) { // error + } +} + +function test5(canAccess: () => Promise | undefined) { + if (!canAccess()) { // ok + } +} + +function test6() { + const x = { + foo: { + async bar() { return true; } + } + }; + + if (!x.foo.bar()) { // error + } +} + +async function test7() { + const x = { + foo: { + async bar() { return true; } + } + }; + + const res = x.foo.bar(); + + if (!res) { // ok + } + + await res; +} + +class Test8 { + async isUser() { + return true; + } + + test() { + if (!this.isUser()) { // error + } + } +} + +class Test9 { + async isUser() { + return true; + } + + async test() { + const res = this.isUser(); + if (!res) { // ok + } + + await res; + } +}