Repository: TypeScript
Issue: Ensure 'in' does not operate on primitive types
Labels: Suggestion,In Discussion

# Bug Report

<!--
  Please fill in each section completely. Thank you!
-->

### 🔎 Search Terms

operator in exception crash unhandled

### 🕗 Version & Regression Information

<!-- When did you start seeing this bug occur?

"Bugs" that have existed in TS for a long time are very likely to be FAQs; refer to
  https://github.com/Microsoft/TypeScript/wiki/FAQ#common-bugs-that-arent-bugs

If possible, please try testing the nightly version of TS to see if it's already been fixed.
For npm: `typescript@next`
This is also the 'Nightly' version in the playground: http://www.typescriptlang.org/play/?ts=Nightly

Note: The TypeScript Playground can be used to try older versions of TypeScript.

Please keep and fill in the line that best applies:
-->
This is the behavior in every version I tried

### ⏯ Playground Link

<!--
  A link to a TypeScript Playground "Share" link which shows this behavior

  The TypeScript Workbench can be used for more complex setups, try
  https://www.typescriptlang.org/dev/bug-workbench/

  As a last resort, you can link to a repo, but these will be slower for us to investigate.
-->
[Playground link with relevant code](https://www.staging-typescript.org/play?ts=4.3.0-pr-43183-11&ssl=1&ssc=1&pln=14&pc=23#code/MYewdgzgLgBAtgV2gIQKYHkBGArVxYC8MAFFABYCWYA5gFwwg55QCUMBAfDOVdQFB9QkWGQCGEANKoAnuxgAeAIIxUADyiowAEwgMm+GAB8YYBABszRmNABOvK6biZUNgDQwJK9Zp3WodmgcEJxcrCGknEDMOYj4YeO5KGnpFVziEgGsZegk0lnpMECjUUTB2LgBvdPiKADMSHkCAMibuaQAHVBB6xup2AiIAckZcfEG2KoSp+CQoNCxRqFIk6hYAbmrpm1QoBBsyrNkqRN41mAB6c5MQFRsbEBtNgF9N7d39mEOYY96zy9v7o8nhs+GJJDJiABGABMAGZ3IMyKgLCBxmcgA)

### 💻 Code

<!-- Please post the relevant code sample here as well-->
```ts
const mustBeObject = (thing: object) => thing

const hasKey = <A extends object | null | string | number, K extends string | number | symbol>(
    thing: A,
    key: K,
): boolean => {
    if (thing && typeof thing === 'object') {
        mustBeObject(thing);
        return key in thing; // no error
    }
    return key in thing; // error
};

hasKey(123, 'hello'); 
```

### 🙁 Actual behavior

TS should expect `A` to be an object

### 🙂 Expected behavior

TS didn't detect the potential crash

---

This is a follow-up to #41317. In #41928, we decided that we should implement a more conservative check that only ensures that the resolved constraint of the right operand to the `in` operator is not assignable to a primitive. This is a negative check ensuring that the type of the right operand does not explicitly extend a primitive type. This solution does not yet cover the original example shown above.

The alternative we discussed was a positive check that the type of the right expression cannot possibly extend a primitive type. We could do this either by checking that the type of right operand (not its resolved constraint!) is not assignable to a primitive or by checking that this type is assignable to `object`.

I am reopening this issue because with #43183 (🎉) the original reason why we went with the more conservative (from a breaking change point of view) check will soon not be a limitation any more.  Therefore, code like

```ts
   if (typeof val === 'object' && '__isMaybe' in val) {
```

does not represent a problem anymore because `val` can be narrowed even if its type is a type parameter as long as it extends a union (see https://github.com/microsoft/TypeScript/pull/41928#issuecomment-743356094 for the original discussion).