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

[bug] switch (true) { case {}: } passes type-check when case is variable #57253

Closed
niedzielski opened this issue Jan 31, 2024 · 5 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@niedzielski
Copy link

niedzielski commented Jan 31, 2024

🔎 Search Terms

"switch(true)" case object type

🕗 Version & Regression Information

  • This is a crash
  • This changed between versions ______ and _______
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about switch and {}
  • I was unable to test this on prior versions because _______

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.4.0-dev.20240131#code/M4dwlgLgxgFgBACggJwK4FMCUcDeAoOOAeiLgAEJgBadADwAd0oIblkB7ZAuKAQ2HS4AvgC5uJcpRoMmLdG07c+AuAHJeAIyiqxQvHijsAdsAhx2GgFZwAvMIPHTcU8ltrN2-aEixEKDNj4hBIU1HSMzKwcXITKghaWYsGkodIRcgoxPPyCLrp4QA

💻 Code

const obj = {}
const str = 'abc'

switch (true) {
  // @ts-expect-error
  case obj: // No good, this is not an error.
  // @ts-expect-error
  case str: // OK, this is an error.
}

🙁 Actual behavior

When a case is an object ({}) variable, the type-check passes. true !== {} so it will never execute.

🙂 Expected behavior

The type-check fails on the always false true === {} comparison.

Additional information about the issue

This appeared to be distinct from:

@DanielRosenwasser
Copy link
Member

Same is true of any other operations that use comparability.

Playground Link

const obj = {}
const str = 'abc'

obj === str // works too

The issue is that you can always write

let x: {} = "hello!";

because {} just describes any value other than null or undefined, and requires no members to be present. It doesn't exclude primitives. The way you could write this instead would be to use the non-primitive type called object.

const obj: object = {}
const str = 'abc'

obj === str // does not work

@niedzielski
Copy link
Author

Thank you, @DanielRosenwasser. In my real code, {} is actually intended to be the Record<never, never> pattern encouraged by the Deno linter. From your docs it sounds like this is always a better alternative for a record intended to have no entries?

@fatcerberus
Copy link

You should probably be careful about bringing up linters banning {} around here, the maintainers have some strong opinions about that one :)

a record intended to have no entries

This kind of thing is really an antipattern in general in TS. You can't prevent { a: string } (or any other object type) from having additional properties (see #12936), so IMO trying to represent "guaranteed-to-be-empty object" in the type system is largely a fool's errand.

btw, from the linked Deno page:

Finally, Object and {} means "any non-nullish value"

And technically, so does { toString(): string }, due to the structural typing machinery. 😉 This is a pretty weak argument against {}, especially if Object is being dragged into it.

...hmm, it seems I may have some opinions about this myself... 😜

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 1, 2024
@RyanCavanaugh
Copy link
Member

The Deno linter is wrong to ban {}. There, I said it 🥰

@niedzielski
Copy link
Author

Thanks all <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants