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
Enhancement: consistent-type-assertions Require satisfies
with the assertion
#7096
Comments
I think the cases where it is absolutely necessary are few and far between in modern TS. What examples to you have of cases where it's "sometimes necessary"?
One could easily argue that the previous code was equally unsafe as it already relied upon a refining assertion to work (which is the reason And that the previous code could have been improved to be completely safe via
In order to do any of this, the rule would require types - which the rule currently does not use. So these would be a non-starter as an addition to Where I'm at right now is that the style this enforces isn't really a whole lot of extra safety because there are still unsafe refining The code is also super verbose and there's many cases where you probably can't properly satisfy the linter - for example if there are anonymous object types or really large anonymous unions - both of which are fairly common cases (which is the same reason many people don't like explicit function return types!). I do see some benefits to this style to those that want it - however I think there's not going to be a whole lot of people who actively want this style due to the verbosity and difficulty naming types. From my experience at scale it's not uncommon for codebases to just allow For a general purpose plugin like ours I just don't see anywhere near enough of our userbase wanting to use this style. Which is why I don't think this belongs here. That's not to say it doesn't deserve to exist, just better in a different (or entirely separate) plugin. I'll leave this open to evaluate community engagement. |
@bradzacher Thanks for the quick reply — and for all your work maintaining typescript-eslint. It's been an invaluable project for all the code I've written recently, so I really appreciate it. In terms of who this rule would be aimed at: I agree with you that there are a lot of teams, especially larger teams with developers of varying skill levels, that just can't realistically forbid Instead, I think this rule would be aimed at the teams that, today, are inclined to ban all assertions using So, this rule would enable teams in the "no assertion" mindset to get more safety in those rare cases where they do choose to use an assertion. It could even be combined with With that preface, below are some random examples where I think a type assertion makes the most sense. These are examples that only fairly sophisticated TS users would run into, with the possible exception of the However, precisely because type assertions should be rare and discouraged, I don't think the verbosity of this style is a major problem, and is arguably even a virtue (if it nudges people on the margin towards something even safer). In the cases where an assertion is still the best option, though, then I think it's reasonable to say "we should still make the assertion as safe as possible", which is what motivated this style. I'm reasonably confident that we could make a rule here that make teams in the 'no assertion' mindset strictly better off, and that would have a low false positive rate (by having carve outs for some of the cases mentioned in my OP, like not needing The examples:
|
You can augment the TS types to provide a definition for You can do this by patching TS, by declaring an ambient global declaration in your workspace, or by using a "node modules lib def" to override the inbuilt TS types. |
Similar to before - you can avoid the need for an assertion everywhere by re-typing Again - no The only case that you can't handle here is if you want to map from a tuple to a tuple for the entires because TS doesn't provide any way to map from a tuple to a tuple, and I don't believe it provides any tooling to "generate" tuple types of indeterminate lengths. |
I'm not sure I understand why you specifically need an Both options don't need an |
This does look like a difficult one given you're butting up against a limitation of TS. But this is definitely a rare edge case as evidenced by the fact that there's just 2 reactions to the issue and it's been open for 3 years. If your codebase is commonly hitting this usecase and requiring an |
@bradzacher For my first example, augmenting the built-in types as you suggest is a great solution. I will use that! For my second example, I don't think the const arr: Array<'a' | 'b' | 'c'> = ['a'];
const x = fromTupleEntries(arr.map((k) => [k, 'foo'])); In the above, TS will think that For the third example, the point of the For the fourth example, all I can say is that these kind of "it's correct but TS can't prove it" cases do come up from time to time, and they create a class of cases where a type assertion is appropriate, no matter how strict a team wants to be. Also, here's a version of the fourth example to show, more realistically (and with better names) how that union type issue can manifest. That said, I'm not sure exactly what you're getting at with your comments. Are you trying to argue that type assertions are never the best option, or just that they should be rare? (Or maybe you're just trying to improve my code, which I appreciate! 😁) If you think they should be rare, then I totally agree, but I still think this rule would improve them in the few places they are used. |
Btw, I did just try to add the overload you suggested for example 1, but it didn't work, unfortunately 😢 The issue seems to have to do with TS' current limitations around unions of call signatures; see the error here, which goes away without the augmented definition. |
This issue has been open for 5 months now and it has not garnered any additional reactions or comments from other users. We take this as signal that it's not a request that is important for the community and as such we are going to reject the proposal for this project. Thanks! |
Before You File a Proposal Please Confirm You Have Done The Following...
My proposal is suitable for this project
Link to the rule's documentation
https://typescript-eslint.io/rules/consistent-type-assertions/
Description
Casting a value to a different type (using
as
) is unsafe, but sometimes necessary. However, usingsatisfies
to state assumptions about the value's type before the assertion, in as much detail as TS can validate, makes the cast much less dangerous as the code evolves.As one example, suppose you have a variable
x
, which TS sees as astring | number
, and you cast it to astring
usingx as string
. Then, suppose the code that producesx
is refactored so thatx
can now be astring | number | boolean
. At that point, the cast is probably incorrect/unsafe, but TS won't issue any errors, and the refactor may well lead to a bug.However, if the original cast were instead
x satisfies string | number as string
, then the refactor would lead to a type error, becausestring | number | boolean
does not satisfystring | number
.Essentially, then,
someVar satisfies T as T2
is a way to castsomeVar
fromT
toT2
; it will fail ifsomeVar
isn't aT
(!), compared to version withoutsatisfies
, which will succeed in cases where it shouldn't. Accordingly, the proposed check would require usingsatisfies
on any expression that has a type assertion.Of course, combining
satisfies
andas
can also make upcasts safe, as was discussed in the issue proposingsatisfies
, whereas an upcast withas
can accidentally be a downcast (esp. after a refactor).This check could be a new option to the
consistent-type-assertions
rule, or it could be its own rule; the latter might allow introducing more customization options down the line — e.g., if we find that there are some assertions where thesatisfies
doesn't really add much, and lint failures would just add a lot of noise. Examples could include:any
, as casting that to something safer shouldn't requiresatisfies
.const y = x as Exclude<typeof x, number>
), as the user is already doing an assertion that's conservative/making minimal assumptions.unknown
, asx satisfies unknown as T
might be a bit unneccessary.As a separate rule, this could also have its own fixer, which would replace
x as Y
withx satisfies [insert type of x] as Y
.Fail
Pass
Additional Info
No response
The text was updated successfully, but these errors were encountered: