The feature of TypeScript that allows expressions to be narrowed based on control flow analysis is typically constrained to local testing, like how typeof x === "string"
can narrow x
down to a string
.
However, TypeScript does not extensively track how one expression affects another or how subsequent tests may interact with the outcomes of earlier tests. The compiler cannot realistically simulate every possible program execution path by hypothetically narrowing unions to each member for all scenarios. Abstract reasoning about counterfactual events also poses challenges.
This limitation of TypeScript has been discussed in GitHub issues, such as microsoft/TypeScript#54252. Solving this logical analysis problem grows exponentially with the complexity of the issue, akin to the Boolean satisfiability problem.
If you find yourself dealing with such code behavior, consider refactoring to conduct tests more directly, even if it involves some redundancy. The approach may vary depending on your specific use cases. For example:
function sample(one: boolean, two?: string) {
const x = one ? "hello" : two;
if (x) {
// Perform desired actions
}
}
Alternatively:
function sample(one: boolean, two?: string) {
if (one) {
doSomethingWithX("hello");
} else if (two) {
doSomethingWithX(two);
}
function doSomethingWithX(x: string) {
// Perform desired actions
}
}
Playground link for code illustration