Latest Results
[ty] Support class decorators (#25091)
## Summary
This PR adds support for class decorator return types, following Carl's
comment in
https://github.com/astral-sh/ty/issues/2604#issuecomment-3793310652:
assume unannotated decorators preserve the class binding when their
result is otherwise `Unknown`, but support explicit spelling of an
annotated decorator return type.
In more detail, the rule is roughly:
- If applying the decorator gives a non-`Unknown` result...
- And that result still retains the original class object, we preserve
the class.
- Otherwise, we replace the public binding with that result.
- If applying the decorator gives an `Unknown` result:
- We fall back to the decorator’s own shape.
- Unannotated function/method decorators are treated as
"identity-preserving", so we keep the current class binding.
- Decorators with an explicit return annotation are not, so we do _not_
preserve the binding.
As a concrete example, an unannotated decorator like the personify
example from #2604 is still treated as preserving the original class
(since we don't have a static replacement type):
```python
def personify(cls):
class Wrapped(cls):
full_name: str
def set_full_name(self, full_name: str) -> None:
self.full_name = full_name
return Wrapped
@personify
class Animal: ...
reveal_type(Animal) # <class 'Animal'>
Animal().set_full_name("John") # error: unresolved attribute
```
But if the decorator has an explicit return type, we use it for the
public class binding:
```python
class WrapBackend:
def __init__(self, cls: type[object]) -> None: ...
def get(self, key: str) -> bytes | None: ...
@WrapBackend
class CacheClient: ...
reveal_type(CacheClient) # WrapBackend
reveal_type(CacheClient.get("x")) # bytes | None
```
This gives us stable behavior for common untyped identity decorators
while leaving the advanced and unsound returned-class inference path out
of scope...
Closes https://github.com/astral-sh/ty/issues/143. Latest Branches
0%
charlie/declaration-order -4%
0%
samuelcolvin:parse-recursion-limit © 2026 CodSpeed Technology