Typescript: try-finally causes "variable is used before being assigned"

Created on 13 Aug 2019  路  13Comments  路  Source: microsoft/TypeScript


TypeScript Version: 3.5.3


Search Terms:

  • variable is used before being assigned
  • try finally variable

Code

function bug() {
  let close: () => void | undefined;
  if (process.env.NODE_ENV === 'development') {
    close = () => {}
  }
  try {
    // ...
  } finally {
    if (close) { // Variable 'close' is used before being assigned.ts(2454)
      close()
    }
  }
}

Expected behavior:

Should compile without errors

Actual behavior:

Does not compile: Variable 'close' is used before being assigned.ts(2454)

Playground Link:

http://www.typescriptlang.org/play/index.html#code/GYVwdgxgLglg9mABAIxAcwBQEpEG8BQiiANgKZSITFwDOpAXItogLwB8iAbnDACYDchRDGBMADgCc4EUjRoA6UmE7yAcgHkAIgFEA+ttUA1VixaIA5L1KdS1MQFslUczgJEiVWqVZMc7PAC+QkFEUBIAnnhCRAD0MYjyicGIwDBgAIbExJFu7iJMnnSu0e6FpNglIYhBAUA

Related Issues:

29684

12205

12355

Question

Most helpful comment

You need to parenthesize your function type. This will fix it.

```diff
- let close: () => void | undefined;
+ let close: (() => void) | undefined;

All 13 comments

You are using it before being assigned in all code paths. If process.env.NODE_ENV === 'development' evaluates to false you never assign any value to close. This issue is completely unrelated to the try-finally block.

For context, this occurs in this case when strictNullChecks is on. Otherwise, not.

You need to parenthesize your function type. This will fix it.

```diff
- let close: () => void | undefined;
+ let close: (() => void) | undefined;

This issue is completely unrelated to the try-finally block.

while that's true

You are using it before being assigned in all code paths

this is not 100% true. What I wrote is still valid JavaScript and TypeScript usually checks such conditions in branches. I wrote an explicit if-statement that checks for undefined in that case.

What's really interesting is that when I change the type from a function to for example a string it works:

function bug() {
  let test: string | undefined; // does not work with () => void | undefined
  if (process.env.NODE_ENV === 'development') {
    test = 'bar'
  }
  if (test) {
    // works 馃憤 
  }
}

and as function arguments it works also:

function bug(close?: () => void) {
  if (process.env.NODE_ENV === 'development') {
    close = () => {}
  }
  try {
    // ...
  } finally {
    if (close) { // works 馃憤 
      close()
    }
  }
}

If you mark something as potentially undefined, it will allow you to use it without initialization. That's why https://github.com/microsoft/TypeScript/issues/32836#issuecomment-520946344 should address the issue.

You need to parenthesize your function type. This will fix it.

- let close: () => void | undefined;
+ let close: (() => void) | undefined;

Oh, damn! 馃う鈥嶁檪 How embarrassing that I overlooked that. Sometimes you do not see the forest for the trees

To be honest it just sort of stinks that we don't provide a better error message or something there.

That would be awesome. After hours of coding such a small typo sometimes leads to a total 馃く

See #32846.

Great. I already gave my 馃憤 and subscribed 馃槑

At the moment I am thinking about if it would be possible that something like this let close: () => void; automatically has an inferred union type of (() => void) | undefined because nothing is assigned at the moment. So it must be initially undefined. What do you think @DanielRosenwasser ? With that you don't have to explicitly annotate it with (() => void) | undefined

The problem with that is that until an assignment occurs, the narrowed type would be undefined, allowing you to pass it in to optional parameters of unrelated types. That might be undesirable, so requiring an explicit | undefined helps make the intent clearer.

P.S. I'm on mobile, so I'm sort of guessing the behavior from memory.

so requiring an explicit | undefined helps make the intent clearer

True. But it's a little bit annoying to always type an | undefined 鈽猴笍 Mabey a let close?: () => void; like in function arguments would be cool 馃槈

@screendriver The current behavior is good, because if you do, say, let x: SomeComplexObject, and then forget to give it a value before using it (because there are many cases where you can't meaningfully initialize something like this directly at declaration), you get a nice error. Effectively, the type says it can't be undefined, so you can ever observe an undefined, it's an error. let x? would be nice, though, and I've sometimes wished for that myself.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

uber5001 picture uber5001  路  3Comments

wmaurer picture wmaurer  路  3Comments

remojansen picture remojansen  路  3Comments

blendsdk picture blendsdk  路  3Comments

dlaberge picture dlaberge  路  3Comments