-
Notifications
You must be signed in to change notification settings - Fork 12.8k
What's happening with strictAny
?
#24737
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
Comments
I would add that, today, expressions of type Throwing ourselves into a world where |
It makes no sense. strictAny must be switched on. |
We hadn't considered thinking about how to work through problems 🤔 |
Of course, a great and good piece of work has already been done. But the good typing system should not allow for such strange behaviours. Perhaps you should consider a section that allows you to "disable" strictAny for a block code? The language "rust" has this nicely solved. |
Just wanted to comment from the Google side. We both (1) eagerly turn on all the strictness flags you provide (we are glad to trade off development time for safety) and (2) use a linter to globally warn about using Whenever we turn on a strictness flag, we turn it on globally in our codebase, then sprinkle 'any' in all the currently-failing locations to make it easy for teams to individually fix their type errors. So I think this proposed strictAny would not be useful for us either, unless we had some other sort of escape hatch type. Preserving PS: we love reading these rationales, thanks for writing it! |
@DanielRosenwasser this seems like a sensible conclusion given the design constraints for TypeScript. I'll share my perspective on a couple points that led us to a different choice in Hack.
TypeScript uses
It is interesting your measure for if it was worth the cost is seeing if there are errors in existing code. The main motivation for us is to prevent future bugs from being introduced. In particular in a strict file for Hack, we've found developers have the expectation that the type checker will warn them when they are making mistakes. Next thing you know, they call a function that returns an The |
Hey @dlreeves! From what you just posted, it definitely sounds like Hack had a good opportunity to introduce
That's actually really interesting. So my understanding is that in Hack, Funny enough, TypeScript does have a suppression comment: For the record, we really enjoyed meeting with you and your team and hearing about the problems you were working on. The fact that some of the original work was inspired by Scala.js, and that we were inspired by your work, makes me happy that we can learn and collaborate. |
For anyone finding this now and wondering if there is a solution, you can use typescript-eslint with the following rules: "rules": {
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error"
} Links to the documentation for these rules can be found here: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules You can also add these rules, plus some others, using the "extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
] You must also add this to your eslint config to allow the eslint plugin find your ts project configuration which enables it to do type-checking: "parserOptions": {
"project": "./tsconfig.json"
}, Here's the full docs for the parser config: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#configuration |
After work on #24423, and discussion at #24593, we've come to the conclusion that we won't be adding a new
--strictAny
flag. Here's the rationale behind it.Background
Recently a few of us met with the Hack team and discussed their proposed
dynamic
type which acts likeany
behaviorally but is as restrictive as our newunknown
type with respect to assignability. In other words, you can dot off ofdynamic
, you can call/constructdynamic
, etc., but you can't assign adynamic
to anything other than anotherdynamic
type. This gives some of the ease-of-use ofany
, but ensures that that usage is strictly scoped, and escape has to be intentional.Goal
We started with a few goals: to bring our users
dynamic
(i.e. a scoped version ofany
)dynamic
type)Rather than introduce a new concept, we opted to introduce a more restrictive mode for
any
that treated it as thisdynamic
type (i.e.--strictAny
). This has the added benefit that users decide how strictany
is rather than declaration authors choosing whether to useany
orunknown
or{}
etc., and I believe that this became the third goal over time.Problems
We experimented with
--strictAny
as a new flag in #24423 and the short story is that while the mode felt like it was catching a lot of questionable code, we started seeing a lot of code that quickly became tough to reason about.Contravariance for
any
-ful signaturesOne of the first things that popped up was that the type
(...args: any[]) => any
was no longer as flexible as before. Since the main change in--strictAny
is to remove one direction of assignability ofany
with everything else, running code with--strictFunctionTypes
meant that almost no functions were assignable to(...args: any[]) => any
anymore.One solution was to have users use a signature like
(...args: never[]) => any
instead (which has issues as we'll see). Instead, we special-cased this construct, though this didn't cover less-contrived signatures like(x: any, xs: any[]) => any
.Users need to know
never
We've always told users that
any
is your escape hatch in TypeScript. Under--strictAny
, you can still access any member on a value of typeany
- but you've lost the ability to say "just trust that I know what I'm doing" with it.As mentioned on #24423 (comment)
never
, while often very useful, is something users have rarely had to think about. Now, it becomes the primary "just trust me" mechanism for type assertions, and that seems undesirable.Places the compiler uses
any
for simplificationToday, the compiler "cheats" in a few places, substituting in
any
when diving into checks that may be very expensive. For example, when comparing signatures from a source type to a target type, if either side has more than one signature, all generic signatures will have their type parameters erased withany
(seegetErasedSignature
inchecker.ts
).But this indicates an assumption that we the language creators made about the laxness of
any
. And under--strictAny
, code like the following fails because those assumptions have been invalidated.So it seems questionable that users could expect this behavior change when the language itself couldn't.
Existing code and
.d.ts
filesBoth
lib.d.ts
and DefinitelyTyped had breaks due to this. This hasn't always been a problem with other strictness flags, but it's something we can't ignore.Didn't prove its worth for the cost
While I hate to be blunt, this is kind of the reality. The feature is useful, but maybe not quite enough given how it works today. I think @RyanCavanaugh summarized this best at #24711 (comment)
Is there any hope?
Alternatives we discussed include:
dynamic
typeunknown
becomes as lax asany
in--strictAny
(or even making that the default)We don't feel like these alternatives are significantly better (or necessarily as good). For that reason, we don't think we're going to pursue
--strictAny
in the near future.The text was updated successfully, but these errors were encountered: