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

Rule clash between @typescript-eslint/init-declarations and no-undef-init #1710

Closed
xandris opened this issue Aug 13, 2024 · 22 comments · Fixed by #1842
Closed

Rule clash between @typescript-eslint/init-declarations and no-undef-init #1710

xandris opened this issue Aug 13, 2024 · 22 comments · Fixed by #1842
Labels

Comments

@xandris
Copy link

xandris commented Aug 13, 2024

Given a stock config, local variables that may be undefined are impossible to lint:

// Variable 'x' should be initialized on declaration
// @typescript-eslint/init-declarations
let x: number | undefined

// It's not necessary to initialize 'x: number | undefined' to undefined
// no-undef-init
let x: number | undefined = undefined

The safer route is probably to turn off no-undef-init and allow superfluous initialization with undefined? At least in Typescript.

@mightyiam
Copy link
Owner

Sorry about this. And thank you for raising it. Will take a look. Perhaps this weekend.

@xandris
Copy link
Author

xandris commented Aug 15, 2024

I actually ended up turning off @typescript-eslint/init-declarations because for our codebase the problem it prevents is already caught by the Typescript compiler (TS2454 Variable is used before being assigned)

@mightyiam
Copy link
Owner

Thank you for the additional feedback, @xandris.

@mightyiam
Copy link
Owner

Analysis with @rostislav-simonik conclusion:
Both rules are desirable, except that typescript-eslint/typescript-eslint#9950.

@xandris would you concur?

We suggest waiting for upstream's response.

@xandris
Copy link
Author

xandris commented Sep 7, 2024

yes, i agree completely

@mightyiam
Copy link
Owner

typescript-eslint/typescript-eslint#9950 (comment)

@xandris say... how many of these do you have? Could they possibly be nicer as null instead?

@darkbasic
Copy link

Upstream locked the issue so I think the lesser evil is to disable init-declarations. At least that's what I did.

@xandris
Copy link
Author

xandris commented Sep 25, 2024

typescript-eslint/typescript-eslint#9950 (comment)

@xandris say... how many of these do you have? Could they possibly be nicer as null instead?

not many... it's usually like an initializer that's too complex for a ternary. the variables are not actually nullable; they really do get assigned before use and the compiler does enforce that

@mightyiam
Copy link
Owner

Thank you @darkbasic and @xandris .

"not many" leads me towards eslint-disable comments. Thoughts on that?

@darkbasic
Copy link

@mightyiam I use let myVar: MyType | undefined = undefined quite a lot in try/catches for example so I wouldn't be so fond of using eslint-disable comments.

@mightyiam
Copy link
Owner

@darkbasic could I possible trouble you to provide some example, please?

@darkbasic
Copy link

@mightyiam for example in the project I'm currently working right now I need to share some variables between different event listeners to make vuetify tables resizable. I need to create these variables upfront and I cannot initialize them with anything else but undefined:

image

@mightyiam
Copy link
Owner

@darkbasic why not null?

@darkbasic
Copy link

@mightyiam there are cases where null might be a valid value that could be set and there would be no way to tell if the variable has been assigned or not.

@darkbasic
Copy link

darkbasic commented Oct 16, 2024

The problem is that until this will be properly fixed Typescript doesn't create a union with undefined for variables that have been declared but not assigned. So you should be forced to assign a value to the variable, even if it's just undefined.

@mightyiam
Copy link
Owner

@darkbasic perhaps it's appropriate in the case you mentioned to disable the rule ad-hoc for some limited scope, or even the entire project. See a recent readme section:
https://github.com/mightyiam/eslint-config-love/blob/01af4999a739cb4ec8c51e67c63f0bf3431f5246/README.md#disabling-rules

I don't see a valid reason to disable the rule, though. Please try to prove me wrong if I'm wrong.

@mightyiam mightyiam closed this as not planned Won't fix, can't repro, duplicate, stale Oct 19, 2024
@darkbasic
Copy link

Let's try the other way around: what do you think is the value of no-undef-init? Because I don't see any and maybe that's skewing the way I weight its benefits.

I don't see a valid reason to disable the rule

The reason is that Typescript is not able to infer undefined from a variable that has not been assigned yet. So you have to be forced to add undefined to the union of the possible variable types to overcome that TS limitation. The only way to force the user to add undefined to the union is via init-declarations, but no-undef-init prevents you from doing so. I value init-declarations much more than no-undef-init for the reasons I've just explained.

@darkbasic
Copy link

darkbasic commented Oct 19, 2024

This is from openapi-typescript documentation:

image

They do the same thing: they assign undefined to the variable as a way to force you to add undefined to its type in order to circumvent the Typescript limitations.

@mightyiam
Copy link
Owner

The value of no-undef-init is in choosing a style in cases where a choice is present.
In cases where initializing with undefined and not initializing is equivalent, then this rule makes the decision for us. That seems to be the entirety of its value.

It seems that in TS, as opposed to in JS, some cases are not equivalent.

Is this correct?

@darkbasic
Copy link

It seems that in TS, as opposed to in JS, some cases are not equivalent.

There are non equivalent cases in JS as well: think of a loop where you are using a non block-scoping statement like var: the variable declaration will be hoisted outside of the loop while the assignment will remain inside the loop.
So this rule goes beyond styling even with plain JS, but it gets worse with Typescript because it prevents you from always assigning a value to the variables that you declare. You should always assign a value (even if it's undefined) because Typescript is still not smart enough to understand if that variable has been initialized or not and thus change its type accordingly. Forcing you do to so means that you will manually add | undefined to its type circumventing this limitation. This has been fixed in the latest Typescript Nightlies but since it didn't make its way into a stable release yet I hadn't a chance to test it and I'm not sure if it covers every possible corner case. So we should still use init-declarations and thus disable no-undef-init for the moment.

@mightyiam mightyiam reopened this Oct 20, 2024
@mightyiam
Copy link
Owner

Thank you, @darkbasic.

mightyiam added a commit that referenced this issue Oct 20, 2024
eslint-config-love-release-bot bot pushed a commit that referenced this issue Oct 20, 2024
## [89.0.1](v89.0.0...v89.0.1) (2024-10-20)

### Bug Fixes

* rm no-undef-init ([7038ca4](7038ca4)), closes [#1710](#1710)
@eslint-config-love-release-bot

🎉 This issue has been resolved in version 89.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Development

Successfully merging a pull request may close this issue.

3 participants