Union types and narrowingintermediate
A discriminated union makes impossible states impossible: one status field the compiler forces you to handle, instead of three booleans that can contradict each other.
Model a request that can fail, handle every state the compiler knows about, and stop firing one per keystroke.
Every state is handled, a rejected fetch becomes an error state, and three rapid calls fire once.
Published in full, before you start — every point is one of these and there is nothing else. Each one runs your code; it is not a search for keywords.
A discriminated union makes impossible states impossible: one status field the compiler forces you to handle, instead of three booleans that can contradict each other.
Every await can reject; modelling idle, loading, success and error explicitly is what stops a slow network showing a blank screen with no explanation.
Firing a search on every keystroke sends ten requests for one query; a debounce waits for the typing to stop, and it is four lines you will reuse forever.