From 622b843860500b461304126482b66dd88ad0cd1d Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Thu, 17 Oct 2024 20:17:04 +0200 Subject: [PATCH] Enable @typescript-eslint/no-unnecessary-condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workarounds: - `false as boolean` to avoid TypeScript saying something is `false` (never `true`) where it doesn’t know that mutation elsewhere can set it back to `true`. - Ignore “unnecessary” `switch` on unions with only one variant – so far. - Ignore places where I do weird gymnastics with untrusted objects (where the types are more constrained than reality). --- client/client.ts | 3 +++ client/proxy.ts | 1 + eslint.config.js | 1 + src/Compile.ts | 7 +++---- src/Errors.ts | 1 + src/Hot.ts | 1 + src/Postprocess.ts | 1 + src/PostprocessWorker.ts | 1 + src/index.ts | 2 ++ tests/Errors.test.ts | 1 + tests/HotHelpers.ts | 21 +++++++++++---------- 11 files changed, 26 insertions(+), 14 deletions(-) diff --git a/client/client.ts b/client/client.ts index c0a3107..206af9a 100644 --- a/client/client.ts +++ b/client/client.ts @@ -21,6 +21,7 @@ import { // Support Web Workers, where `window` does not exist. const window = globalThis as unknown as Window; +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const IS_WEB_WORKER = window.window === undefined; // These used to be separate properties on `window`, like @@ -159,6 +160,7 @@ const DEFAULT_ELM_WATCH: __ELM_WATCH = { let { __ELM_WATCH } = window; +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (typeof __ELM_WATCH !== "object" || __ELM_WATCH === null) { // Each property is added later below. __ELM_WATCH = {} as unknown as __ELM_WATCH; @@ -168,6 +170,7 @@ if (typeof __ELM_WATCH !== "object" || __ELM_WATCH === null) { } for (const [key, value] of Object.entries(DEFAULT_ELM_WATCH)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (__ELM_WATCH[key as keyof __ELM_WATCH] === undefined) { (__ELM_WATCH as Record)[key] = value; } diff --git a/client/proxy.ts b/client/proxy.ts index 86fdbbc..49037fe 100644 --- a/client/proxy.ts +++ b/client/proxy.ts @@ -19,6 +19,7 @@ error.elmWatchProxy = true; const existing = window.Elm; const existingObject = + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition typeof existing === "object" && existing !== null ? existing : undefined; const elmProxy = new Proxy(existingObject ?? {}, { diff --git a/eslint.config.js b/eslint.config.js index 97cab83..f3ec3f6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -79,6 +79,7 @@ export default typescriptEslint.config( "@typescript-eslint/no-shadow": error, "@typescript-eslint/no-this-alias": warn, "@typescript-eslint/no-unnecessary-boolean-literal-compare": warn, + "@typescript-eslint/no-unnecessary-condition": error, "@typescript-eslint/no-unnecessary-parameter-property-assignment": warn, "@typescript-eslint/no-unnecessary-template-expression": error, "@typescript-eslint/no-unnecessary-type-arguments": warn, diff --git a/src/Compile.ts b/src/Compile.ts index a7a1d7a..899594a 100644 --- a/src/Compile.ts +++ b/src/Compile.ts @@ -116,7 +116,7 @@ export function installDependencies( ); // Avoid printing `loadingMessage` if there’s nothing to download. - let didWriteLoadingMessage = false; + let didWriteLoadingMessage = false as boolean; const timeoutId = setTimeout(() => { logger.write(loadingMessage); didWriteLoadingMessage = true; @@ -691,7 +691,7 @@ async function compileOneOutput({ // Watcher events that happen while waiting for `elm make` and // postprocessing can flip `dirty` back to `true`. - outputState.dirty = false; + outputState.dirty = false as boolean; const { promise, kill } = SpawnElm.make({ elmJsonPath, @@ -1630,8 +1630,7 @@ function targetNameEmojiTweak( return { targetName, delta: 0 }; } - /* v8 ignore next */ - const content = match[0] ?? ""; + const content = match[0]; // Avoid emoji on Windows, for example. if (!loggerConfig.fancy) { diff --git a/src/Errors.ts b/src/Errors.ts index 34d5825..75db6c5 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -1746,6 +1746,7 @@ ${text(errorFilePath.content)} function printUnknownValueAsString(value: UnknownValueAsString): Piece { switch (value.tag) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition case "UnknownValueAsString": return text(value.value); } diff --git a/src/Hot.ts b/src/Hot.ts index be207ec..504b02f 100644 --- a/src/Hot.ts +++ b/src/Hot.ts @@ -1459,6 +1459,7 @@ const runCmd = // Retry writing it. writeElmWatchStuffJson(mutable); // If still an error, print it. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (mutable.elmWatchStuffJsonWriteError !== undefined) { logger.write(""); logger.errorTemplate( diff --git a/src/Postprocess.ts b/src/Postprocess.ts index aee0faa..1ee705f 100644 --- a/src/Postprocess.ts +++ b/src/Postprocess.ts @@ -255,6 +255,7 @@ class PostprocessWorker { this.worker.on("message", (message: MessageFromWorker) => { switch (message.tag) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition case "PostprocessDone": switch (this.status.tag) { /* v8 ignore start */ diff --git a/src/PostprocessWorker.ts b/src/PostprocessWorker.ts index f0c3cab..f7e75fb 100644 --- a/src/PostprocessWorker.ts +++ b/src/PostprocessWorker.ts @@ -39,6 +39,7 @@ function main(port: PortWrapper): void { port.on("message", (message: MessageToWorker) => { switch (message.tag) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition case "StartPostprocess": elmWatchNode(message.args) .then((result) => { diff --git a/src/index.ts b/src/index.ts index d8604b5..1217c6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -93,6 +93,7 @@ export async function elmWatchCli( ); } while (result.tag === "Restart"); switch (result.tag) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition case "Exit": return result.exitCode; } @@ -126,6 +127,7 @@ if (require.main === module) { // happens it should at least be possible to exit with a simple ctrl+c. // Note: `.setRawMode` is `undefined` when stdin is not a TTY, but this is // not reflected in the type definitions. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (process.stdin.setRawMode !== undefined) { process.stdin.setRawMode(false); } diff --git a/tests/Errors.test.ts b/tests/Errors.test.ts index afca92c..898a0d9 100644 --- a/tests/Errors.test.ts +++ b/tests/Errors.test.ts @@ -1713,6 +1713,7 @@ describe("errors", () => { encoding: "utf8", }, ); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!(result.error === undefined || result.error === null)) { throw result.error; } diff --git a/tests/HotHelpers.ts b/tests/HotHelpers.ts index d0f8e06..c680f19 100644 --- a/tests/HotHelpers.ts +++ b/tests/HotHelpers.ts @@ -39,6 +39,7 @@ const hotKillManager: HotKillManager = { kill: undefined }; export async function cleanupAfterEachTest(): Promise { const { currentTestName } = expect.getState(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (window.__ELM_WATCH?.KILL_MATCHING !== undefined) { // The idea is that we need no logging here – it’ll just result in double // logging since there will most likely be a running server as well. @@ -528,7 +529,7 @@ export function collapseUi(targetName?: string): void { function expandUiHelper(wantExpanded: boolean, targetName?: string): void { withShadowRoot((shadowRoot) => { - const button = shadowRoot?.querySelector( + const button = shadowRoot.querySelector( `${ targetName === undefined ? "[data-target]" @@ -547,7 +548,7 @@ function expandUiHelper(wantExpanded: boolean, targetName?: string): void { export function showErrors(targetName?: string): void { withShadowRoot((shadowRoot) => { - const button = shadowRoot?.querySelector( + const button = shadowRoot.querySelector( `${ targetName === undefined ? "[data-target]" @@ -564,7 +565,7 @@ export function showErrors(targetName?: string): void { export function hideErrors(targetName?: string): void { withShadowRoot((shadowRoot) => { - const button = shadowRoot?.querySelector( + const button = shadowRoot.querySelector( `${ targetName === undefined ? "[data-target]" @@ -581,7 +582,7 @@ export function hideErrors(targetName?: string): void { export function closeOverlay(): void { withShadowRoot((shadowRoot) => { - const button = shadowRoot?.querySelector( + const button = shadowRoot.querySelector( `[data-test-id="OverlayCloseButton"]`, ); if (button instanceof HTMLElement) { @@ -595,7 +596,7 @@ export function closeOverlay(): void { export function getOverlay(): string { let result = "(Overlay not found)"; withShadowRoot((shadowRoot) => { - const overlay = shadowRoot?.querySelector(`[data-test-id="Overlay"]`); + const overlay = shadowRoot.querySelector(`[data-test-id="Overlay"]`); if (overlay instanceof HTMLElement) { const children = Array.from(overlay.children, (child, index) => { const clone = child.cloneNode(true) as HTMLElement; @@ -617,7 +618,7 @@ export function getOverlay(): string { export function clickFirstErrorLocation(): void { withShadowRoot((shadowRoot) => { - const button = shadowRoot?.querySelector(`[data-test-id="Overlay"] button`); + const button = shadowRoot.querySelector(`[data-test-id="Overlay"] button`); if (button instanceof HTMLButtonElement) { button.click(); } else { @@ -629,7 +630,7 @@ export function clickFirstErrorLocation(): void { export function moveUi(position: BrowserUiPosition): void { expandUi(); withShadowRoot((shadowRoot) => { - const button = shadowRoot?.querySelector( + const button = shadowRoot.querySelector( `button[data-position="${position}"]`, ); if (button instanceof HTMLButtonElement) { @@ -643,7 +644,7 @@ export function moveUi(position: BrowserUiPosition): void { export function switchCompilationMode(compilationMode: CompilationMode): void { expandUi(); withShadowRoot((shadowRoot) => { - const radio = shadowRoot?.querySelector( + const radio = shadowRoot.querySelector( `input[type="radio"][value="${compilationMode}"]`, ); if (radio instanceof HTMLInputElement) { @@ -657,7 +658,7 @@ export function switchCompilationMode(compilationMode: CompilationMode): void { export function assertCompilationMode(compilationMode: CompilationMode): void { expandUi(); withShadowRoot((shadowRoot) => { - const radio = shadowRoot?.querySelector(`input[type="radio"]:checked`); + const radio = shadowRoot.querySelector(`input[type="radio"]:checked`); if (radio instanceof HTMLInputElement) { expect(radio.value).toStrictEqual(compilationMode); } else { @@ -671,7 +672,7 @@ export function assertCompilationMode(compilationMode: CompilationMode): void { export function assertDebugDisabled(): void { expandUi(); withShadowRoot((shadowRoot) => { - const radio = shadowRoot?.querySelector('input[type="radio"]'); + const radio = shadowRoot.querySelector('input[type="radio"]'); if (radio instanceof HTMLInputElement) { expect(radio.disabled).toBe(true); } else {