Well, deprecation in Python usually implies that at some future point the function (etc.) won't exist at all, so we can see this as type-checking for multiple versions at once.
Yeah this is fair. I'm a bit skeptical about the feasibility of cleanly formalizing the notion of "multi-version checking" with types and constraint systems though.
I'm not quite happy about this, since unfamiliar readers could read it as `foo4` returns something deprecated (as opposed to `foo4` itself is deprecated).
Yes this is one aspect I dislike about the proposal: types go with the return values, not the function. Interpreting the type as if it were applying to the entire function does not seem to be compatible with how the rest of the type system works.
What would this form of syntactical hints be though? And how it would be lighter-weight than say, a typing.Deprecated type?
When I used the word "lightweight", I did not mean the syntactical verbosity of your construct. Instead, I was referring to how much more complexity the construct is going to introduce to the type system. Ideally I would like to see this feature adding zero complexity: no subtyping rule change or constraint system tweaking is needed. To put it in another way, type checkers could simply throw all the wrapping `typing.Deprecated` away, and their core behaviors wouldn't be affected. The pyright proposal would be a good example of this. There's no need to specially attach or assign new types to the decorator. For parameters and global attributes, I'm OK with your `typing.Deprecated` proposal as long as it's only used for syntactical marking purpose -- i.e. they can only appear on param and global annotations, must be used as outermost location (how does this work with `typing.Final` which has the same requirement?), and cannot appear as annotations for local variables / return types / class attributes, etc. What I would suggest against is to assign special type-level semantics to this `typing.Deprecated` construct. - Jia