question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

typeguards only narrow types when used with unary not

See original GitHub issue

Bug 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

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:open
  • Created 2 years ago
  • Reactions:2
  • Comments:10 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
RyanCavanaughcommented, Jun 1, 2021

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.

1reaction
Craigfiscommented, Jun 4, 2021

I just ran into a similar (perhaps the same?) issue. Given:

const isA = (x: A | B): x is A => Object.prototype.hasOwnProperty.call(x, 'kind');
const isB = (x: A | B): x is B => !isA(x);

isA() narrows to A (when true) or B (when false). isB() narrows to A | B (when true) or never (when false).

Playground

Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - Narrowing
This analysis of code based on reachability is called control flow analysis, and TypeScript uses this flow analysis to narrow types as it...
Read more >
TypeScript: narrowing types via type guards and assertion ...
The Array method .every() does not narrow #. If we use .every() to check that all Array elements are non-nullish, TypeScript does ...
Read more >
Do type guards only narrow the type when true is returned?
@DBS, yes, if the only options are Fish and Birth that is a valid assumption. But what if those are not the only...
Read more >
5 Methods to use Type Guards in TypeScript
Type Guards come into the picture with the usage of TypeScript. This technique is used to identify or narrow down the type of...
Read more >
How to use type guards in TypeScript
The typeof type guard is said to be very limited and shallow. It can only determine the following types recognized by JavaScript: boolean...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found