Home > other >  Forcing a while(true) loop to exit in TypeScript
Forcing a while(true) loop to exit in TypeScript

Time:09-23

I'm trying to trick test coverage calculation to work with while true loops. The coverage tools I am using consider test coverage to be lacking due to the while ending case is never executed. This is obviously a bug in the coverage calculation. However, I am trying to invent something that works until the coverage calculation gets fixed upstream, or at least until a suitable ignore comment is implemented.

The problematic code looks as follows. This works fine and tests work fine. However, I get the missing code coverage due to the while loop never exiting.

function* greetings1(): Generator<string, never, undefined> {
  while(true) {
    yield 'hello'
  }
}

I explored the idea of creating a constant TRUE that would let specially crafted tests to exit the loops by mocking the constant TRUE and replacing it with a false value that exists the loop. However, this gives me a type error A function returning 'never' cannot have a reachable end point. ts(2534).

const TRUE: true = true
function* greetings2(): Generator<string, never, undefined> {
  while(TRUE) {
    yield 'hello'
  }
}

Is it possible to add an escape hatch for exiting the while true loop without having to sacrifice the types and result to casting the entire function body? I would be fine doing a tricky cast with the constant TRUE if needed.

I also tried some tricks using break. However, the coverage tool considers the use of break to be different from the condition becoming false, so that does not entirely solve my problem. Is there is a solution, it probably needs to be based making the while loop end naturally.

I posted a related question about a specific code coverage tool one year ago. See Istanbul code coverage : Ignore else branch of a while true loop

CodePudding user response:

Ugh, that's a tricky problem. You have one tool that wants to see the loop possibly exit (the test coverage tool), and another that wants to see it never exit (TypeScript).

Sadly, in this particular case, I think your best bet is a ts-expect-error annotation:

const TRUE: true = true;
// @ts-expect-error 2534
function* greetings2(): Generator<string, never, undefined> {
    while (TRUE) {
        yield "hello";
    }
}

Playground link

That tells TypeScript that you expect the code following it to give you that error and only that error, and it should ignore it. If the situation changes such that the code doesn't produce that error, or it produces some other error, TypeScript will complain, so it's much better than @ts-ignore.

I really don't like doing that (or type assertions, which it effectively is), but in this situation with the tool conflict, I don't think you get a lot of choice.

CodePudding user response:

Here is something else that might work:

const TRUE: true = true
function* greetings2(): Generator<string, never, undefined> {
  while(TRUE) {
    yield 'hello'
  }
  throw new Error("Unreachable");
}

Playground link

Throwing an exception unconditionally after the loop should be enough to convince Typescript that the function never exits, and it will not affect the normal behavior of the program.

Whether your coverage tool likes it or not is another question I guess :)

  • Related