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

Type validations do not carry over when assigned to a const #48891

Closed
oguzgelal opened this issue Apr 30, 2022 · 4 comments
Closed

Type validations do not carry over when assigned to a const #48891

oguzgelal opened this issue Apr 30, 2022 · 4 comments

Comments

@oguzgelal
Copy link

oguzgelal commented Apr 30, 2022

Bug Report

🔎 Search Terms

  • Object is possibly 'undefined' when it shouldn't be
  • TypeScript fails to detect when a value is not undefined
  • TypeScript fails to detect when a value is not null
  • TypeScript fails to detect when a value is not nil
  • TypeScript fails to detect non-falsy values

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about

⏯ Playground Link

Playground link with relevant code

💻 Code

type Obj = {
  test1?: string
  test2: string | null
  test3: string | false
}

const obj: Obj = {
  test1: "hello",
  test2: "hello",
  test3: "hello",
}

function fn(arg: string) {
  /** blah */
}

// A - ✓ 
// expected: error
fn(obj.test1)
fn(obj.test2)
fn(obj.test3)

// B - ✓
// expected: no error
if (!!obj.test1) fn(obj.test1)
if (!!obj.test2) fn(obj.test2)
if (!!obj.test3) fn(obj.test3)

// C - ✓ 
// expected: no error
if (typeof obj.test1 === "string") fn(obj.test1)
if (typeof obj.test2 === "string") fn(obj.test2)
if (typeof obj.test3 === "string") fn(obj.test3)

// D - x
// expected: no error
// behavior: error
const ok1 = !!obj.test1
const ok2 = !!obj.test2
const ok3 = !!obj.test3
if (ok1) fn(obj.test1)
if (ok2) fn(obj.test2)
if (ok3) fn(obj.test3)

// E - x
// expected: no error
// behavior: error
const ok4 = typeof obj.test1 === "string"
const ok5 = typeof obj.test2 === "string"
const ok6 = typeof obj.test3 === "string"
if (ok4) fn(obj.test1)
if (ok5) fn(obj.test2)
if (ok6) fn(obj.test3)

🙁 Actual behavior

TypeScript can tell when the value of a variable is not falsy inside of an if block where the validation is directly used as the condition. But it fails to carry it over when the outcome of the validation is assigned to a const and that variable is used as the condition of the if statement instead.

🙂 Expected behavior

As long as the outcome of the validation is assigned to a const, TypeScript should deduce that the value is not falsy.

@MartinJohns
Copy link
Contributor

It's working as intended. See #44730.

Narrowing through indirect references occurs only when the conditional expression or discriminant property access is declared in a const variable declaration with no type annotation, and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body.

@oguzgelal
Copy link
Author

oguzgelal commented Apr 30, 2022

@MartinJohns Thanks for pointing out the PR! Exactly the issue I'm facing.

In my code sample above, could you help me understand why there are errors in D and E when the expression is assigned to a const and obj is also const (and not re-assigned) ?

@MartinJohns
Copy link
Contributor

Because of this part:

and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body.

obj.test1 (and the other two) don't refer to a const variable, a readonly property or a parameter.

@oguzgelal
Copy link
Author

oguzgelal commented Apr 30, 2022

Ah I see! Got it. Thank you very much!

For reference, making Obj type Readonly solved the issue.

type Obj = Readonly<{
  test1?: string
  test2: string | null
  test3: string | false
}>

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants