typeguards only narrow types when used with unary not
See original GitHub issueBug Report
🔎 Search Terms
- negation
- typeguard
- unary not
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about narrowing/typeguards
The FAQ entry with the most promising title is a TODO
: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-isfoox-narrow-x-to-foo-when-isfoo-is-a-type-guard
This occurs on every version of Typescript available in the playground, including nightly.
⏯ Playground Link
💻 Code
interface Entity {
type: string;
}
const ACTOR_TYPE = 'actor';
interface Actor extends Entity {
type: typeof ACTOR_TYPE;
}
function isActor(entity: Entity): entity is Actor {
return entity.type === ACTOR_TYPE;
}
const foo: Actor = {type: 'actor'}; // valid, as expected
const bar: Actor = {type: 'not-that'}; // invalid, as expected
const bin: Entity = {type: 'item'}; // valid, not an actor
if (isActor(bin) === false) {
typeof bin; // is Entity, as expected
} else {
typeof bin; // is Entity, not as expected
}
if (isActor(bin) == false) {
typeof bin;
} else {
typeof bin; // unexpected Entity, same as above
}
if (!isActor(bin)) {
typeof bin; // is Entity, as expected
} else {
typeof bin; // is Actor, as expected
}
🙁 Actual behavior
Comparing the result of a typeguard with guard(it) === false
or == false
does not narrow the type, but the unary operator with !guard(it)
does narrow it to Actor
.
🙂 Expected behavior
This may be due to a subtlety with the entity is Actor
return type not being a proper boolean, but I would expect all forms of negation and comparison to false
to narrow the type, and https://www.typescriptlang.org/docs/handbook/2/narrowing.html#equality-narrowing seems to support that. For a typeguard that returns a boolean, the comparisons should be equivalent. Is there some reason that is not the case here?
I’m working with lint rules that discourage unary not (easy to miss) and discovered this while doing some refactoring.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:10 (3 by maintainers)
Top GitHub Comments
I’m much more sympathetic to
=== false
(which at least does something) than=== true
, my own personal tastes notwithstanding. I’d be inclined to support this form if we heard more feedback on it.I just ran into a similar (perhaps the same?) issue. Given:
isA() narrows to A (when true) or B (when false). isB() narrows to A | B (when true) or never (when false).
Playground