Closed
Description
There are numerous questions as to why any
cannot be narrowed from. Despite being natural and intuitive this feature was rejected due to being a serious breaking change to poorly written yet massive code bases.
Please consider putting it behind a flag provided the evident need for it.
TLDR
any
is not what you think it is (poor naming at play)- for narrowing use
type unknown = {} | undefined | null | void
instead - yes, it's a hot unfortunate mess
- no, there is no painless way to fix it
- yes, you gonna need to fix your
*.d.ts
by hands - why? to make average JS developers happy with their less than perfect code
- welcome to the club! get your badge at the reception
UPDATE
@yortus made a branch per #9999 (comment) where narrowing from any
works!
try it: npm install typescript-narrowany
Metadata
Metadata
Assignees
Labels
Type
Projects
Relationships
Development
No branches or pull requests
Activity
jesseschalken commentedon Jul 28, 2016
Can you specify a use case?
any
is already compatible with everything and everything is compatible with it, so why do you need to narrow it?The whole point of
any
is to opt out of static typing. If you don't want to opt out, but want a type that can be anything but which you have to narrow before assuming it is something, that's what{}
is for.This code passes:
and so does this:
yortus commentedon Jul 28, 2016
@jesseschalken here are some use cases:
A. Narrowing the error variable in a
catch
block, which is of typeany
and can't be annotated. The user did not 'opt-out' of type checking here, they just have no choice.B. Retaining type safety when we explicitly use type guards on an
any
variable:C. Refactoring using tools like VSCode
Suppose in the following example we use the VSCode 'rename' tool to rename
Dog#woof
toDog#bark
. The rename will appear to work but will miss the guardedwoof
reference. The code will still compile without errors, but crash at runtime.EDIT: Adding a fourth use case that came up later in #9999 (comment):
D. Consuming definitions from
lib.d.ts
and other third-party libraries.lib.d.ts
and many (most?) other type declaration files on DefinitelyTyped useany
to mean "this value could be anything". These values cannot be narrowed in consumer code, even though the consumer did not ask to opt-out of type checking.yortus commentedon Jul 28, 2016
I think @RyanCavanaugh summed up the issues around narrowing
any
:However, TypeScript currently only caters to the one set of users.
👍 for a flag to cater to both sets of users, since both groups have valid needs.
RyanCavanaugh commentedon Jul 28, 2016
Serious question @Aleksey-Bykov -- how are there even any
any
s left in your codebase?jesseschalken commentedon Jul 28, 2016
@yortus
A. Eeek. That's unfortunate. There should definitely be a compiler option to make caught errors
{}
instead ofany
.B & C. I see. The problem then is that TypeScript enables you to mix dynamic and static typing, but you can't have the same variable be dynamically typed in one place and statically typed in another place. Dynamic vs static is a property of the variable itself and you can't transition in/out of dynamic mode without creating a new variable or using
as
with every reference, or redirecting references through a getter.I don't care personally because I try to stay as far away from
any
as possible and consider the solution to any problems withany
to be to simply remove or insulate myself from it. If the use case of mixed dynamic/static typing wrt the same variable is sufficiently common for mixed static/dynamic codebases I guess it's a worthy concern.RyanCavanaugh commentedon Jul 28, 2016
Unorganized thoughts, some contradictory
{}
, or as{ [s: string]: any}
and using string indexer notation to access unsafe properties, or writingconst xx: {} = x;
and narrowing on that once you're doing unsafe things withx
. Seeing "real" code that can't reasonably accommodate those workarounds would be useful.T extends any
where you get strictness on the known properties ofT
butany
s when accessing other properties. That seems like the both-sides-win behavior that avoids a new flag but accomplishes most of what's desired here: any
or: { }
on acatch
variable. Why not.any
. I'd certainly turn that rule on for all my projects: { }
/: any
type annotations on catch variables #10000yortus commentedon Jul 28, 2016
It's possibly just me, but I find
{}
to be a visually-confusing annotation that makes code intent less clear.x: {}
just looks at a glance like the code is saying 'x is an Object instance', when in reality it means 'x is absolutely anything but don't opt out of type checking'. So it might be an object, array, number, boolean, RegExp, string, symbol, etc.Probably a stupid suggestion but is it worth considering adding a type keyword that's exactly like
{}
but more readable? Perhapsx: unknown
or similar?jesseschalken commentedon Jul 28, 2016
You can do
right now if you want.
yortus commentedon Jul 28, 2016
True, as long as you don't mind adding boilerplate for than in every file you need it. Also
type
aliases lose their pretty name in compiler messages and intellisense.zpdDG4gta8XKpMCd commentedon Jul 28, 2016
@RyanCavanaugh
but you are right, not that many left
jesseschalken commentedon Jul 28, 2016
@yortus It depends how your project is set up, but if you define it at the top level, outside a module or namespace, it will be available across the entire project without needing to be imported, just like the things in lib.d.ts are. Fair point about compiler messages/intellisense though.
zpdDG4gta8XKpMCd commentedon Jul 28, 2016
@RyanCavanaugh
one more case where we have
any
is for functions that deal with arbitrary input likedeepCopy
orareEqual
yortus commentedon Jul 28, 2016
@jesseschalken I assume you are pointing out this is how things currently are, not how they necessarily should be. From #5930 it's appears the team had no problem with the same variable transitioning from untyped to typed in the explicitly protected region of a type guard. After all, their first instinct was to implement narrowing from
any
.It only got backed out when a subset of users, whose code is written according to the old proverb "just as all
Animal
s areDog
s, so all non-Animal
s must beHouse
s", were offended by the compiler pointing out the flaw in their logic, and would rather silence the compiler than fix their code.Having said that, their reasoning that "I used
any
to opt-out of all type checking" is also reasonable. However I agree with the team's apparent first instinct, that sureany
does opt you out of type checking, but a type guard is a nice explicit way of saying "opt me back in".117 remaining items
unknown
: less-permissive alternative toany
#10715{}
type toany
type gcanti/fp-ts#179UnhandledRejectionListener
reason
param type DefinitelyTyped/DefinitelyTyped#24417symbolof
type operator, likekeyof
but for unique symbol properties #20721