[ty] Apply class decorators via try_call() (#22375)

## Summary

Decorators are now called with the class as an argument, and the return
type becomes the class's type. This mirrors how function decorators
already work.

Closes https://github.com/astral-sh/ty/issues/2313.
This commit is contained in:
Charlie Marsh
2026-01-04 17:11:00 -05:00
committed by GitHub
parent 11b551c2be
commit 92a2f2c992
2 changed files with 64 additions and 2 deletions

View File

@@ -234,3 +234,38 @@ def takes_no_argument() -> str:
@takes_no_argument
def g(x): ...
```
## Class decorators
Class decorator calls are validated, emitting diagnostics for invalid arguments:
```py
def takes_int(x: int) -> int:
return x
# error: [invalid-argument-type]
@takes_int
class Foo: ...
```
Using `None` as a decorator is an error:
```py
# error: [call-non-callable]
@None
class Bar: ...
```
A decorator can enforce type constraints on the class being decorated:
```py
def decorator(cls: type[int]) -> type[int]:
return cls
# error: [invalid-argument-type]
@decorator
class Baz: ...
# TODO: the revealed type should ideally be `type[int]` (the decorator's return type)
reveal_type(Baz) # revealed: <class 'Baz'>
```