Skip to content

Using strictNullChecks with/without noImplicitAny changes type inferrence for let variables with null or undefined initial values #55265

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
eliasm307 opened this issue Aug 4, 2023 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@eliasm307
Copy link

eliasm307 commented Aug 4, 2023

πŸ”Ž Search Terms

  • noImplicitAny strictNullChecks

Also might be related to #45592

πŸ•— Version & Regression Information

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

⏯ Playground Link

https://www.typescriptlang.org/play?strict=false&strictFunctionTypes=false&strictPropertyInitialization=false&strictBindCallApply=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&esModuleInterop=false&declaration=false&noErrorTruncation=true&noImplicitOverride=false&ts=5.2.0-beta#code/DYUwLgBAHhC8EDsCuxgG4CwAoA9DiEAegPzbaiQCecESCAJiAGYCWCI92eBJQA

πŸ’» Code

For the following code:

let x = null;
//  ^?

let y = undefined
//  ^?

With just strictNullChecks enabled in tsconfig.json the values are inferred as null and undefined (see playground)

However with strictNullChecks and noImplicitAny the types are now both inferred as any (see playground)

πŸ™ Actual behavior

With strictNullChecks and noImplicitAny let variables with null or undefined initial values are inferred as any type

πŸ™‚ Expected behavior

With strictNullChecks and noImplicitAny let variables with null or undefined initial values should be inferred as null and undefined respectfully to match the normal strictNullChecks behaviour.

noImplicitAny is meant to improve type strictness however with this behaviour it actually means some types become less strict and could allow runtime issues that would have been caught without the option.

I am migrating some repositories to use noImplicitAny incrementally, however I noticed when a package (lets call it ParentPackage) that had noImplicitAny off was using a dependency package (lets call it ChildPackage) that had noImplicitAny on then I was getting type errors in node_modules.

This was due to cases like in the example code where a variable has an initial null value that gets re-assigned, however since the behaviour in ParentPackage without noImplicitAny is to infer the type as null there was an is not assignable to type 'null' type error, where the ChildPackage repo didnt have this error with noImplicitAny

@eliasm307 eliasm307 changed the title Using strictNullChecks with/without noImplicitAny changes type inferrence for let variables with null and undefined initial values Using strictNullChecks with/without noImplicitAny changes type inferrence for let variables with null or undefined initial values Aug 4, 2023
@MartinJohns
Copy link
Contributor

This is working as intended. See #17651 (comment) and other issues (search for strictNullChecks noImplicitAny in:title).

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Aug 4, 2023
@eliasm307
Copy link
Author

Ah ok I see, I'll close this then

@eliasm307 eliasm307 closed this as not planned Won't fix, can't repro, duplicate, stale Aug 4, 2023
@fatcerberus
Copy link

@eliasm307 To be clear, it's not actually inferring any for these variables, but rather what's typically called an "evolving type":
https://www.typescriptlang.org/play?&code/DYUwLgBAHhC8EDsCuxgG4CwAoA9DiEAegPzYDGA9ggM4WgB0wFA5gBRQCUmu%20Bf-JbNlCQAnnAhIEAExAAzAJYIQ07HgKCslGnRCMWrUVzW9%20A0liA=#code/DYUwLgBAHhC8EDsCuxgG4CwAoA9DiEAegPzYDGA9ggM4WgB0wFA5gBRQCUmu+Bf-JbNlCQAnnAhIEAExAAzAJYIQ07HgKCslGnRCMWrUVzW9+A0liA

The rationale is that if you're initializing something with null or undefined, you probably want to assign something else later. So instead the type is inferred on use based on previous assignments. IntelliSense just says any for lack of a better thing to call it. Which of course is confusing, but there's an issue about that: #54414.

@eliasm307
Copy link
Author

@fatcerberus thanks for the clarification and the details, I appreciate it.

I did understand something similar to that from MartinJohns comment however it's interesting learning about how TS is working under the hood. Thanks to you both for the information!

@eliasm307
Copy link
Author

eliasm307 commented Aug 8, 2023

@fatcerberus Also, for interest, I see your name alot in discussions on this project (issues etc) and you seem very knowledgeable but I see you don't have a maintainer tag, are you a former maintainer or an enthusiast?

@fatcerberus
Copy link

I'm not a maintainer, no. But you can learn a lot about how TypeScript works internally by hanging around here for a while and seeing how the maintainers respond to issues--things get blocked by design limitations a lot so the devs tend to be pretty transparent about what goes on under the hood πŸ˜„

@eliasm307
Copy link
Author

@fatcerberus thats cool, you must have been "hanging around" here for quite a while then πŸ˜‚

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants