Skip to content
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

Predicate inference fails for empty object and nullish values #58967

Closed
fnky opened this issue Jun 21, 2024 · 3 comments
Closed

Predicate inference fails for empty object and nullish values #58967

fnky opened this issue Jun 21, 2024 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@fnky
Copy link

fnky commented Jun 21, 2024

🔎 Search Terms

"infer predicate object types", "infer predicate empty object", "infer predicate empty object null", "{} | null"

🕗 Version & Regression Information

  • This is a type narrowing issue
  • This is the behavior in every version I tried

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.5.2#code/MYewdgzgLgBApgWwA5QJ4DkCuAbbMC8MA2gN4C+ANDGDtgLoB0AZgJbZRwBOAFBAQHwwAhEIgBKANwwA9NPgAPJHGAcAJjHJE6AKBh7ZMAHoB+baEixEKVAFUwquKzBx1hUpRiZ7jls9WNWdi5eAWFRSRk5OEVlNQ0yLV19ORMzcGgFJGwWYBYoLFwWCAALAmJyKhpcALYOHj58QT4hfEIq7AiDEABrJMijU3MMqzQsBAAjLjL3KgAGGqD60JFxKS7evX7UoctkNAAVTkwoYtRp8gW6kMaw1f7opRUXeMTNg2303esAMQBDbAgZzcF2YtWCDUE4TWURiT3UzgAblxXskBmkLNRwABRPYYWjnGBMEAgABc8RgHnal3Byyh-R6fXeg0+1FoRVKbnaVC8DicLmpSxuK06MMecURyJ0bxSpiAA

💻 Code

const emptyNull = [{}, null].filter(s => !!s); // expected {}[]
   // ^? ({} | null)[]
const emptyUndefined = [{}, undefined].filter(s => !!s); // expected {}[]
   // ^? ({} | undefined)
const emptyTruthy = [{}].filter(s => !!s); // expected {}[]
   // ^? {}[]
const emptyFalsy = [{}].filter(s => !s); // expected never[]
   // ^? {}[]
const nullish = [null, undefined].filter(s => !!s); // expected never[]
   // ^? (null | undefined)[]
const explicitNullish = [{}, null].filter(s => s !== null); // ok
   // ^? { foo: {}; }[]
const nonEmptyNull = [{ foo: {} }, null].filter(s => !!s); // ok
   // ^? { foo: {}; }[]
const emptyNumber = [{}, 0].filter(s => !!s); // ok
   // ^? {}[]

🙁 Actual behavior

Predicate inference fails for the cases where {} is declared along with a nullish value, using a predicate to test for truthiness or falsiness (!!x / !x). It infers a type that includes the nullishness.

It also fails to infer the predicate for cases where array only contains nullish values.

It works if the object has at least one property, or using explicit bullishness check.

🙂 Expected behavior

The return type should reflect the truthiness of {}, as it is neither a nullish or falsy value and should pass the truthiness test.

Additional information about the issue

The section "This is a gentle nudge away from truthiness footguns" in #57465 explains that object types should have no footguns:

If you're working with object types, on the other hand, there is no footgun
and a truthiness test will infer a predicate:

const datesTruthy = [new Date(), null, new Date(), null].filter(d => !!d);
//    ^? const datesTruthy: Date[]

That's why I expected the same for {} and the opposite with nullish-only values.

@fatcerberus
Copy link

Primitives are assignable to {}, unlike (most) other object types.

@jcalz
Copy link
Contributor

jcalz commented Jun 22, 2024

And because "" and 0 both match {}, you can't say that !x implies that x is null. And without direct support for #15048 you can't use a type predicate for this at all. You'd like to say that (x: {} | null) => !!x returns (x is {}) else false or something, and redefine filter() so it only cares about the true side of the type guard. But we don't have that so 🤷‍♂️

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jun 24, 2024
@typescript-bot
Copy link
Collaborator

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jun 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants
@jcalz @fnky @fatcerberus @RyanCavanaugh @typescript-bot and others