Redundant 'await' on a non-promise can give a false positive

  • What language is this for?
    TypeScript
  • Which rule?
    typescript:S4123
  • Why do you believe it’s a false-positive?
    I can write tests that prove the await is needed
  • Are you using
    • SonarQube - Community Edition Version 9.9.1 (build 69595)
  • How can we reproduce the problem? Give us a self-contained snippet of code (formatted text, no screenshots)
export class NoErrorThrownError extends Error {}

export class TestUtils {
    /**
     * A method to be able to get round eslint-jest
     * Avoid calling `expect` conditionally`  jest/no-conditional-expect
     *
     * @param call the function to call that an error is expected to be thrown.
     * @returns The error that was thrown or NoErrorThrownError if no error was thrown.
     */
    public static getError = async <TError>(
        call: () => PromiseLike<unknown> | unknown
    ): Promise<TError> => {
        try {
            // Do NOT remove the await here, it is needed as the call maybe awaitable.
            // The tests for this function proves it.
            await call();
            throw new NoErrorThrownError();
        } catch (error) {
            return error as TError;
        }
    };
}

Background: We use eslint-plugin-jest and one of their linter rules is no-conditional-expect

As part of testing errors that have been thrown we use this utility method, the call argument may or may not be awaitable. We have to await it in case it is awaitable.

Here are tests that fail if you remove the await from await call(); but fail to compile if you change to use promises all the time.

import { NoErrorThrownError, TestUtils } from './test-utils';

class MyTestError extends Error {}

describe('TestUtils', () => {
    describe('getError()', () => {
        it('throws NoErrorThrownError', async () => {
            let called = false;
            const error = await TestUtils.getError((): void => {
                called = true;
            });
            expect(called).toBe(true);
            expect(error).toBeInstanceOf(NoErrorThrownError);
        });

        it('throws NoErrorThrownError on async calls', async () => {
            let called = false;
            const error = await TestUtils.getError(async (): Promise<void> => {
                return new Promise<void>((resolve) => {
                    setTimeout(() => {
                        called = true;
                        resolve();
                    }, 100);
                });
            });
            expect(called).toBe(true);
            expect(error).toBeInstanceOf(NoErrorThrownError);
        });
    });
});

Hi Alan,

Thank you very much for reporting this issue! It is indeed a false positive and I have created a ticket for our team to fix this - Fix FP S4123 (`no-invalid-await`): do not raise issue for `unknown` type · Issue #3966 · SonarSource/SonarJS · GitHub

1 Like