Skip to content

Type predicates that are not biconditional #61740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
6 tasks done
gavinwahl opened this issue May 20, 2025 · 2 comments
Closed
6 tasks done

Type predicates that are not biconditional #61740

gavinwahl opened this issue May 20, 2025 · 2 comments

Comments

@gavinwahl
Copy link

πŸ” Search Terms

predicate narrowing

βœ… Viability Checklist

⭐ Suggestion

When I have a conditional that narrows a type in the TRUE branch but does not narrow in the FALSE branch, I have no way to extract the condition into a function and preserve the narrowing. That is, I have an implication of the form a => b without !a => !b. Existing type predicates require a <=> b.

The existing type predicate functionality doesn't help me because the compiler assumes they can be inverted. I have no way to narrow the type only if my predicate returns true, but do no narrowing when it returns false.

πŸ“ƒ Motivating Example

function interestingDifferenceInline(x?: number, y?: number) {
  if (x !== undefined && x > 12 && y !== undefined && y > 12) {
    // x and y correctly have type number here, undefined narrowed away.
    // however, my logic is duplicated.
    return x - y;
  } else {
    // x and y are not narrowed here, still have type undefined | number.
    return undefined;
  }
}

// I should be able to extract the duplicated logic into a function
// and get the same type narrowing
function interestingDifferenceExtracted(x?: number, y?: number) {
  if (isInteresting(x) && isInteresting(y)) {
    // TYPE ERROR: x and y should have type number here, but have number | undefined.
    return x - y;
  } else {
    return undefined;
  }
}

function isInteresting(x: number | undefined): boolean {
  // if this function returns true, x should be narrowed to number,
  // if this function returns false, it should not be narrowed at all.
  // We have implication in one direction, but not its inverse.
  return x !== undefined && x > 12;
}

// this is wrong:
function isInterestingIncorrectPredicate(x?: number): x is number {
  // this correctly narrows to number if we do if(isInterestingIncorrectPredicate(x)),
  // but incorrectly narrows to undefined if we do if(!isInterestingIncorrectPredicate(x))
  // type predicates are 'necessary and sufficient', but our condition is
  // 'sufficient but not necessary'. That is, we don't know that x is NOT a
  // number if this predicate returns false.
  return x !== undefined && x > 12;
}

// what we want is some kind of inverse predicate, something like:
// returning true gives us type information, returning false does not.
// function isInterestingDesired(x?: number): x is not undefined {
  // if this function returns true x is narrowed to number, excluding undefined.
  // if this function returns false nothing is narrowed.
// }

πŸ’» Use Cases

I want to improve my code quality by extracting complex, duplicated code into a function without losing type information.

@gavinwahl gavinwahl changed the title Type predicates that are sufficient but not necessary Type predicates that are not biconditional May 20, 2025
@RyanCavanaugh
Copy link
Member

See #15048

@gavinwahl
Copy link
Author

thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy