Skip to content

Unsafe access to uninitialized variables is allowed in any function #30053

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

Closed
AlexGalays opened this issue Feb 22, 2019 · 9 comments
Closed

Unsafe access to uninitialized variables is allowed in any function #30053

AlexGalays opened this issue Feb 22, 2019 · 9 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@AlexGalays
Copy link

TypeScript Version: 3.3.1

Search Terms:
uninitialized variable, unsafe access

Code

function blowup() {
    ohNo.toLocaleLowerCase();
}

let ohNo: string;

blowup();

Expected behavior:
This should not compile when using strictNullChecks

Actual behavior:
It compiles, probably because the flow analysis abandonned.
This convenience is not worth the safety tradeoff, the compiler should enforce that ohNo be typed as string | undefined since the initialization code is not immediately following.

Playground Link:

https://www.typescriptlang.org/play/#src=function%20blowup()%20%7B%0A%20%20%20%20ohNo.toLocaleLowerCase()%3B%0A%7D%0A%0Alet%20ohNo%3A%20string%3B%0A%0Ablowup()%3B

Related Issues:
Not really. It's somewhat related to the old bugs around class property initialization not being enforced.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Feb 26, 2019
@RyanCavanaugh
Copy link
Member

We can't tell this example apart from

function blowup() {
    ohNo.toLocaleLowerCase();
}
function rescue() {
    ohNo = "";
}

let ohNo: string;
rescue();
blowup();

because we don't inline the flow-control effects of functions

@AlexGalays
Copy link
Author

AlexGalays commented Feb 26, 2019

Sure! But then why allow that type declaration in the first place?
I'd say it should be consistent with class property initialization which must be either inline or done in the constructor... and since we don't have a constructor there... :)

@RyanCavanaugh
Copy link
Member

Which part would be theoretically disallowed?

@AlexGalays
Copy link
Author

AlexGalays commented Feb 27, 2019

To make it easy for the compiler (and for the developers who maintain this code! after all, levels of indirection like a function call to initialize the var is just as difficult to follow for a real person), I would say:

"If a variable is not initialized immediately after its declaration (not following function calls flow), force it to be declared with | undefined"

So the disallowed thing here would be to declare ohNo as just a string

This would remain valid:

let ohYes: string;

if (Math.random() > 0.5) ohYes = 'biiig'
else ohYes = 'nope'

@RyanCavanaugh
Copy link
Member

I'm unclear on the definition of "immediate" here. The Math.random call could indirect into code that observes ohYes; we have no way of knowing.

The line has to be drawn somewhere between "always allowed" and "always disallowed"; I agree we could shift that line a little bit one way or another, but moving it to fix one example but break others isn't really ideal in terms of not introducing random breaking changes.

@AlexGalays
Copy link
Author

"immediate" as in, direct code statements, not function calls, since functions seem to be the current limitation?
I'm happy to see this as a gray area with a cursor somewhere but isn't typescript moving toward more strictness in general? (sometimes behind flags)
It wouldn't break anything if it was under a flag, similar to strictPropertyInitialization wouldn't it?

@RyanCavanaugh
Copy link
Member

"immediate" as in, direct code statements, not function calls, since functions seem to be the current limitation

What I mean is, the ohYes variable is not immediately initialized in your example, because there's an intervening possibly-undefined-observing function call between its declaration and its definite initialization. I suspect that will often be the case even for code that is well-formed.

We could add a flag but this could also be syntactically enforced by a lint rule, so I'd generally defer to that route, especially since the definition of "immediate" is really up for debate.

@TenPotatoes
Copy link

I've been wondering recently what things in Typescript can cause runtime errors even with everything strict, I guess this is one 😱

I expected this to be an error in strict mode, it should be required to be string | undefined if not initialized on the same line.

let ohNo: string;

Any complex initialization logic can be done in a function

let ohNo: string = computeOhNo();

function computeOhNo() {
   if (Math.random() > 0.5) { 
     return 'biiig'; 
   }
   return 'nope';
}

@lo1tuma
Copy link

lo1tuma commented Sep 14, 2022

Isn’t this a duplicate of #23305?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants