diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd24f9be315ee..8dfde45e6fd28 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -42919,7 +42919,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function helper(condExpr: Expression, body: Expression | Statement | undefined) { - const location = isLogicalOrCoalescingBinaryExpression(condExpr) ? skipParentheses(condExpr.right) : condExpr; + const location = isLogicalOrCoalescingBinaryExpression(condExpr) ? skipParentheses(condExpr.right) + : isPrefixUnaryExpression(condExpr) ? condExpr.operand + : condExpr; if (isModuleExportsAccessExpression(location)) { return; } @@ -42936,7 +42938,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // the block as a heuristic to identify the most common bugs. There // are too many false positives for values sourced from type // definitions without strictNullChecks otherwise. - const callSignatures = getSignaturesOfType(type, SignatureKind.Call); + const testedType = isPrefixUnaryExpression(condExpr) ? checkExpression(location) : type; + const callSignatures = getSignaturesOfType(testedType, SignatureKind.Call); const isPromise = !!getAwaitedTypeOfPromise(type); if (callSignatures.length === 0 && !isPromise) { return; diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 9801e54397c0f..6bec71170192e 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -996,7 +996,7 @@ function tryGetModuleNameFromRootDirs(rootDirs: readonly string[], moduleFileNam } function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, canonicalSourceDirectory }: Info, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined { - if (!host.fileExists || !host.readFile) { + if (!host.readFile) { return undefined; } const parts: NodeModulePathParts = getNodeModulePathParts(path)!; @@ -1145,7 +1145,6 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan } function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) { - if (!host.fileExists) return; // We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory const extensions = flatten(getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }])); for (const e of extensions) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 32d7af09c03e7..4f84f9b27f49c 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -131,7 +131,7 @@ let pollingChunkSize = createPollingIntervalBasedLevels(defaultChunkLevels); export let unchangedPollThresholds = createPollingIntervalBasedLevels(defaultChunkLevels); function setCustomPollingValues(system: System) { - if (!system.getEnvironmentVariable) { + if (system.getEnvironmentVariable === undefined) { return; } const pollingIntervalChanged = setCustomLevels("TSC_WATCH_POLLINGINTERVAL", PollingInterval); diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 1c0339f7c1488..6e7d8b4b4ccc7 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -1182,8 +1182,7 @@ function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: Com } function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string): readonly string[] { - if (!host.readFile || !host.fileExists) return emptyArray; - + if (host.readFile === undefined || host.fileExists === undefined) return emptyArray; const result: string[] = []; for (const packageJson of findPackageJsons(scriptPath, host)) { const contents = readJson(packageJson, host as { readFile: (filename: string) => string | undefined; }); // Cast to assert that readFile is defined diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0b2072f449008..6f10f3f9d36d4 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -3670,10 +3670,9 @@ export function findPackageJson(directory: string, host: LanguageServiceHost): s /** @internal */ export function getPackageJsonsVisibleToFile(fileName: string, host: LanguageServiceHost): readonly ProjectPackageJsonInfo[] { - if (!host.fileExists) { + if (host.fileExists === undefined) { return []; } - const packageJsons: ProjectPackageJsonInfo[] = []; forEachAncestorDirectory(getDirectoryPath(fileName), ancestor => { const packageJsonFileName = combinePaths(ancestor, "package.json"); diff --git a/tests/baselines/reference/contextualOverloadListFromArrayUnion.errors.txt b/tests/baselines/reference/contextualOverloadListFromArrayUnion.errors.txt new file mode 100644 index 0000000000000..7def0f968e165 --- /dev/null +++ b/tests/baselines/reference/contextualOverloadListFromArrayUnion.errors.txt @@ -0,0 +1,69 @@ +three.ts(28,14): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + + +==== one.ts (0 errors) ==== + declare const y: never[] | string[]; + export const yThen = y.map(item => item.length); +==== two.ts (0 errors) ==== + declare const y: number[][] | string[]; + export const yThen = y.map(item => item.length); +==== three.ts (1 errors) ==== + // #42504 + interface ResizeObserverCallback { + (entries: ResizeObserverEntry[], observer: ResizeObserver): void; + } + interface ResizeObserverCallback { // duplicate for effect + (entries: ResizeObserverEntry[], observer: ResizeObserver): void; + } + + const resizeObserver = new ResizeObserver(([entry]) => { + entry + }); + // comment in #35501 + interface Callback { + (error: null, result: T): unknown + (error: Error, result: null): unknown + } + + interface Task { + (callback: Callback): unknown + } + + export function series(tasks: Task[], callback: Callback): void { + let index = 0 + let results: T[] = [] + + function next() { + let task = tasks[index] + if (!task) { + ~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + callback(null, results) + } else { + task((error, result) => { + if (error) { + callback(error, null) + } else { + // must use postfix-!, since `error` and `result` don't have a + // causal relationship when the overloads are combined + results.push(result!) + next() + } + }) + } + } + next() + } + + series([ + cb => setTimeout(() => cb(null, 1), 300), + cb => setTimeout(() => cb(null, 2), 200), + cb => setTimeout(() => cb(null, 3), 100), + ], (error, results) => { + if (error) { + console.error(error) + } else { + console.log(results) + } + }) + \ No newline at end of file diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional.errors.txt b/tests/baselines/reference/uncalledFunctionChecksInConditional.errors.txt index 4a57e071d84c8..3a2f380d2e6d9 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional.errors.txt +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional.errors.txt @@ -1,17 +1,22 @@ -uncalledFunctionChecksInConditional.ts(5,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(9,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(9,14): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(13,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(32,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(36,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(40,22): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(44,16): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -uncalledFunctionChecksInConditional.ts(48,22): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(6,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(10,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(10,14): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(14,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(33,10): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(37,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(41,22): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(45,16): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(49,22): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(58,6): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(62,6): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? +uncalledFunctionChecksInConditional.ts(66,6): error TS2801: This condition will always return true since this 'Promise' is always defined. +uncalledFunctionChecksInConditional.ts(74,6): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? -==== uncalledFunctionChecksInConditional.ts (9 errors) ==== +==== uncalledFunctionChecksInConditional.ts (13 errors) ==== declare function isFoo(): boolean; declare function isBar(): boolean; + declare function isFooPromise(): Promise; declare const isUndefinedFoo: (() => boolean) | undefined; if (isFoo) { @@ -82,4 +87,33 @@ uncalledFunctionChecksInConditional.ts(48,22): error TS2774: This condition will if (x && z) { // no error z(); + } + + if (!isFoo) { + ~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + // error on isFoo + } + + if (!isFoo || !isFoo()) { + ~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + // error on isFoo + } + + if (!isFooPromise()) { + ~~~~~~~~~~~~~~ +!!! error TS2801: This condition will always return true since this 'Promise' is always defined. +!!! related TS2773 uncalledFunctionChecksInConditional.ts:66:6: Did you forget to use 'await'? + // ts2801 error on isFooPromise + } + + if (!isUndefinedFoo) { + // no error + } + + if (!isFooPromise) { + ~~~~~~~~~~~~ +!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead? + // error on isFooPromise } \ No newline at end of file diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional.js b/tests/baselines/reference/uncalledFunctionChecksInConditional.js index d08d23bfacbc6..91f2a1c9a5ada 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional.js +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional.js @@ -3,6 +3,7 @@ //// [uncalledFunctionChecksInConditional.ts] declare function isFoo(): boolean; declare function isBar(): boolean; +declare function isFooPromise(): Promise; declare const isUndefinedFoo: (() => boolean) | undefined; if (isFoo) { @@ -55,6 +56,26 @@ if (ux || y || uz || isFoo) { if (x && z) { // no error z(); +} + +if (!isFoo) { + // error on isFoo +} + +if (!isFoo || !isFoo()) { + // error on isFoo +} + +if (!isFooPromise()) { + // ts2801 error on isFooPromise +} + +if (!isUndefinedFoo) { + // no error +} + +if (!isFooPromise) { + // error on isFooPromise } //// [uncalledFunctionChecksInConditional.js] @@ -92,3 +113,18 @@ if (x && z) { // no error z(); } +if (!isFoo) { + // error on isFoo +} +if (!isFoo || !isFoo()) { + // error on isFoo +} +if (!isFooPromise()) { + // ts2801 error on isFooPromise +} +if (!isUndefinedFoo) { + // no error +} +if (!isFooPromise) { + // error on isFooPromise +} diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional.symbols b/tests/baselines/reference/uncalledFunctionChecksInConditional.symbols index 62555c4ba35c8..1c21e9e2c66a7 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional.symbols +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional.symbols @@ -7,8 +7,12 @@ declare function isFoo(): boolean; declare function isBar(): boolean; >isBar : Symbol(isBar, Decl(uncalledFunctionChecksInConditional.ts, 0, 34)) +declare function isFooPromise(): Promise; +>isFooPromise : Symbol(isFooPromise, Decl(uncalledFunctionChecksInConditional.ts, 1, 34)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + declare const isUndefinedFoo: (() => boolean) | undefined; ->isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 2, 13)) +>isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 3, 13)) if (isFoo) { >isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) @@ -31,7 +35,7 @@ if (isFoo || isFoo()) { } if (isUndefinedFoo || isFoo()) { ->isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 2, 13)) +>isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 3, 13)) >isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) // no error @@ -45,25 +49,25 @@ if (isFoo && isFoo()) { } declare const x: boolean; ->x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 24, 13)) +>x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) declare const ux: boolean | undefined; ->ux : Symbol(ux, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) +>ux : Symbol(ux, Decl(uncalledFunctionChecksInConditional.ts, 26, 13)) declare const y: boolean; ->y : Symbol(y, Decl(uncalledFunctionChecksInConditional.ts, 26, 13)) +>y : Symbol(y, Decl(uncalledFunctionChecksInConditional.ts, 27, 13)) declare const uy: boolean | undefined; ->uy : Symbol(uy, Decl(uncalledFunctionChecksInConditional.ts, 27, 13)) +>uy : Symbol(uy, Decl(uncalledFunctionChecksInConditional.ts, 28, 13)) declare function z(): boolean; ->z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 27, 38)) +>z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 28, 38)) declare const uz: (() => boolean) | undefined; ->uz : Symbol(uz, Decl(uncalledFunctionChecksInConditional.ts, 29, 13)) +>uz : Symbol(uz, Decl(uncalledFunctionChecksInConditional.ts, 30, 13)) if (x || isFoo) { ->x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 24, 13)) +>x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) >isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) // error on isFoo @@ -71,43 +75,74 @@ if (x || isFoo) { if (isFoo || x) { >isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) ->x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 24, 13)) +>x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) // error on isFoo } if (x || y || z() || isFoo) { ->x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 24, 13)) ->y : Symbol(y, Decl(uncalledFunctionChecksInConditional.ts, 26, 13)) ->z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 27, 38)) +>x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) +>y : Symbol(y, Decl(uncalledFunctionChecksInConditional.ts, 27, 13)) +>z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 28, 38)) >isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) // error on isFoo } if (x || uy || z || isUndefinedFoo) { ->x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 24, 13)) ->uy : Symbol(uy, Decl(uncalledFunctionChecksInConditional.ts, 27, 13)) ->z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 27, 38)) ->isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 2, 13)) +>x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) +>uy : Symbol(uy, Decl(uncalledFunctionChecksInConditional.ts, 28, 13)) +>z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 28, 38)) +>isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 3, 13)) // error on z } if (ux || y || uz || isFoo) { ->ux : Symbol(ux, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) ->y : Symbol(y, Decl(uncalledFunctionChecksInConditional.ts, 26, 13)) ->uz : Symbol(uz, Decl(uncalledFunctionChecksInConditional.ts, 29, 13)) +>ux : Symbol(ux, Decl(uncalledFunctionChecksInConditional.ts, 26, 13)) +>y : Symbol(y, Decl(uncalledFunctionChecksInConditional.ts, 27, 13)) +>uz : Symbol(uz, Decl(uncalledFunctionChecksInConditional.ts, 30, 13)) >isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) // error on isFoo } if (x && z) { ->x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 24, 13)) ->z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 27, 38)) +>x : Symbol(x, Decl(uncalledFunctionChecksInConditional.ts, 25, 13)) +>z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 28, 38)) // no error z(); ->z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 27, 38)) +>z : Symbol(z, Decl(uncalledFunctionChecksInConditional.ts, 28, 38)) +} + +if (!isFoo) { +>isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) + + // error on isFoo +} + +if (!isFoo || !isFoo()) { +>isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) +>isFoo : Symbol(isFoo, Decl(uncalledFunctionChecksInConditional.ts, 0, 0)) + + // error on isFoo +} + +if (!isFooPromise()) { +>isFooPromise : Symbol(isFooPromise, Decl(uncalledFunctionChecksInConditional.ts, 1, 34)) + + // ts2801 error on isFooPromise +} + +if (!isUndefinedFoo) { +>isUndefinedFoo : Symbol(isUndefinedFoo, Decl(uncalledFunctionChecksInConditional.ts, 3, 13)) + + // no error +} + +if (!isFooPromise) { +>isFooPromise : Symbol(isFooPromise, Decl(uncalledFunctionChecksInConditional.ts, 1, 34)) + + // error on isFooPromise } diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional.types b/tests/baselines/reference/uncalledFunctionChecksInConditional.types index 3c1d9e9690a1c..b8d38a3fa7518 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional.types +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional.types @@ -7,6 +7,9 @@ declare function isFoo(): boolean; declare function isBar(): boolean; >isBar : () => boolean +declare function isFooPromise(): Promise; +>isFooPromise : () => Promise + declare const isUndefinedFoo: (() => boolean) | undefined; >isUndefinedFoo : (() => boolean) | undefined @@ -132,3 +135,43 @@ if (x && z) { >z() : boolean >z : () => boolean } + +if (!isFoo) { +>!isFoo : false +>isFoo : () => boolean + + // error on isFoo +} + +if (!isFoo || !isFoo()) { +>!isFoo || !isFoo() : boolean +>!isFoo : false +>isFoo : () => boolean +>!isFoo() : boolean +>isFoo() : boolean +>isFoo : () => boolean + + // error on isFoo +} + +if (!isFooPromise()) { +>!isFooPromise() : false +>isFooPromise() : Promise +>isFooPromise : () => Promise + + // ts2801 error on isFooPromise +} + +if (!isUndefinedFoo) { +>!isUndefinedFoo : boolean +>isUndefinedFoo : (() => boolean) | undefined + + // no error +} + +if (!isFooPromise) { +>!isFooPromise : false +>isFooPromise : () => Promise + + // error on isFooPromise +} diff --git a/tests/cases/compiler/uncalledFunctionChecksInConditional.ts b/tests/cases/compiler/uncalledFunctionChecksInConditional.ts index 0e9b555725a1a..622b695a80a31 100644 --- a/tests/cases/compiler/uncalledFunctionChecksInConditional.ts +++ b/tests/cases/compiler/uncalledFunctionChecksInConditional.ts @@ -2,6 +2,7 @@ declare function isFoo(): boolean; declare function isBar(): boolean; +declare function isFooPromise(): Promise; declare const isUndefinedFoo: (() => boolean) | undefined; if (isFoo) { @@ -54,4 +55,24 @@ if (ux || y || uz || isFoo) { if (x && z) { // no error z(); +} + +if (!isFoo) { + // error on isFoo +} + +if (!isFoo || !isFoo()) { + // error on isFoo +} + +if (!isFooPromise()) { + // ts2801 error on isFooPromise +} + +if (!isUndefinedFoo) { + // no error +} + +if (!isFooPromise) { + // error on isFooPromise } \ No newline at end of file