Repository: TypeScript
Issue: Bug: null can be excluded by !== null in flow before typeof === 'object', but not excluded by predicate function.
Labels: Help Wanted,Possible Improvement

### 🔎 Search Terms

"null", "control flow", "narrowing", "narrow", "exclusion", "object", "typeof"

### 🕗 Version & Regression Information

Potentially, we could expect the behaviors of `if (a === null)` and `if ((v => v !== null)(a))` (inferred as predicate since TS 5.5) should be the same, but it's actually not in specific cases.

If it's combined with `typeof a === 'object'` check in the same flow, first one is correctly omits the null possibility, while second one doesn't.

[<img width="540" alt="image" src="https://github.com/user-attachments/assets/3b3e2417-4912-456d-b2dc-b0dad8dec016">](https://www.typescriptlang.org/play/?ts=5.6.2#code/MYewdgzgLgBAlhAcgVwDapgXhgCgG4BcMyYA1mCAO5gCUWAfDHlptmGqgNwBQA9LzEEwAegH5uoSLABmWXAEMiJclVoMYAb25D4snAhToc8mnQBOAUyjIzYHjrh6oATwAOFkLPkwAhKxgA5CAARgBWFsBQAeZWNnbaQvL2-CLiAL483BLg0DAA5nLGSmQU1HSYjFoOet6sbBwx1rb2Qo64Lu6eMN5+2EFhEVGNcS2CSdopYtwZQA)


- This changed between versions:
  - I confirmed that the exclusion in flow can be done since TS4.5. It's not regression, but the new feature seems to have been partially applied.
  - TS4.4 https://www.typescriptlang.org/play/?ts=4.4.4#code/MYewdgzgLgBAlhAcgVwDapgXhgCgG4BcMyYA1mCAO5gCURe8EMYaGmAfDA5j86wNwAoAPTCY4mAD0A-INCRYAMyy4AhkRLkqtLJwDegifGU4EKdDlU0aMAE4BTKMltghRuCagBPAA72QyqowAIS8AOQgAEYAVvbAUGE2Dk4ubhKqbqJSsgC+QoJy4NAwAOYqlhpkFNQ2HDAG7iZBPNgs6EmOzq6GEh643n4BMEGh2BExcQkdKd1GGYZZMoJ5QA
  - TS4.5 https://www.typescriptlang.org/play/?ts=4.5.5#code/MYewdgzgLgBAlhAcgVwDapgXhgCgG4BcMyYA1mCAO5gCURe8EMYaGmAfDA5j86wNwAoAPTCY4mAD0A-INCRYAMyy4AhkRLkqtLJwDegifGU4EKdDlU0aMAE4BTKMltghRuCagBPAA72QyqowAIS8AOQgAEYAVvbAUGE2Dk4ubhKqbqJSsgC+QoJy4NAwAOYqlhpkFNQ2HDAG7iZBPNgs6EmOzq6GEh643n4BMEGh2BExcQkdKd1GGYZZMoJ5QA
- This changed in commit or PR (not sure for now, sorry)

### ⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.6.2#code/MYewdgzgLgBAlhAcgVwDapgXhgCgG4BcMyYA1mCAO5gCUWAfDHlptmGqgNwBQA9LzEEwAegH5uoSLABmWXAEMiJclVoMYAb25D4snAhToc8mnQBOAUyjIzYHjrh6oATwAOFkLPkwAhKxgA5CAARgBWFsBQAeZWNnbaQvL2-CLiAL483BLg0DAA5nLGSmQU1HSYjFoOet6sbBwx1rb2Qo64Lu6eMN5+2EFhEVGNcS2CSdopYtwZQA

### 💻 Code

```ts
const isNull = (v: unknown) => v === null;
//    ^?
const f = (a: unknown) => {
    if (isNull(a)) return;
    if (typeof a !== 'object') return;
    a;
 // ^?
};


const g = (a: unknown) => {
    if (a === null) return;
    if (typeof a !== 'object') return;
    a;
 // ^?
};
```


### 🙁 Actual behavior

Last `a` in `f` is inferred as `object | null`.

### 🙂 Expected behavior

Last `a` in `f` would be inferred as `object`.

### Additional information about the issue

Here is important to check `typeof === 'object'` after the check for null, because type narrowing step is formally, `unknown` → `object | null` → `object`. `=== null` alone cannot narrow anything on `unknown`.

[If you swap the if-statements in above, you can see both is inferred as `object`.](https://www.typescriptlang.org/play/?ts=5.6.2#code/MYewdgzgLgBAlhAcgVwDapgXhgCgG4BcMyYA1mCAO5gCUWAfDHlptmGqgNwBQA9LzEEwAegH5uoSLABmWXAEMiJclVoMYAb25D4snFACeABwCmIWfJgBCVjADkIAEYArE8Ch26AJxNRkXsB4dOD0EFHQceRpvX39A7SF5IP4RcQBfHm4JcGgYAHM5SKUyCmo6TEYtYL1DU3MYSxtsBxc3Dxi-AKChEIUWNg4OuO7BJO0UsW4MoA)