I recently encountered a unit test that looked like this.
The use of branching and conditionals in tests is an anti-pattern since we want tests to be predictable, each test to focus on a single code execution path and generally keep things simple.
The obvious solution is to replace the conditional with .to.not.be.null or .to.not.be.undefined or the catch-all .to.exist (less cognitive overhead). So why was this code written using an if and throw in the first place?
The answer is that asserting existence of the object here causes TypeScript error TS2532: Object is possibly 'undefined'..
This is because the implementation in chai creates an assertion object and evaluates it, then an error is thrown if the assertion fails. TypeScript can’t infer that the .to.exist check will throw if the object is null.
This is not a new problem and a proposal for asserting control flow has been discussed in TypeScript#8655 and an implementation proposed in TypeScript#32695.
Assert Not Null
The first solution is a more elegant variation if the original if and throw.
Unfortunately, TypeScript as of now doesn’t infer the conditional inside a function, either, so, you need to wrap the call and ensure it returns an object to make this option work.
We get an error instead of an assertion.
We can augment assertNotNull with an expect to get a proper assertion failure instead of an error.
The result is better.
Casting a Type
We can cast the result of reticulate() and TypeScript will happily let us by.
This is problematic. If the signature of reticulate() were to change, we would just be forcing the response to pretend to be a Spline, getting no new compile-time errors and leaving nonsensical tests.
Finally, we can use TypeScript ! and explicitly check .to.exist.
This is my preferred method, but requires disabling strictNullChecks in tests (read more about this here).