microsoft / microsoft/TypeScript

Bug: null can be excluded by !== null in flow before typeof === 'object', but not excluded by predicate function.

Open
#60,102 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Domain: check: Control Flow Help Wanted Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 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.

image

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.6.2#code/MYewdgzgLgBAlhAcgVwDapgXhgCgG4BcMyYA1mCAO5gCUWAfDHlptmGqgNwBQA9LzEEwAegH5uoSLABmWXAEMiJclVoMYAb25D4snAhToc8mnQBOAUyjIzYHjrh6oATwAOFkLPkwAhKxgA5CAARgBWFsBQAeZWNnbaQvL2-CLiAL483BLg0DAA5nLGSmQU1HSYjFoOet6sbBwx1rb2Qo64Lu6eMN5+2EFhEVGNcS2CSdopYtwZQA

💻 Code
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, unknownobject | nullobject. === null alone cannot narrow anything on unknown.

If you swap the if-statements in above, you can see both is inferred as object.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the TypeScript Playground links and the minimal reproductions for f and g, comparing the inferred types after the typeof check. Trace the control-flow narrowing and inferred predicate behavior responsible for the difference; done means the predicate case narrows a to object without null, with regression coverage for the reported example.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.