ty icon indicating copy to clipboard operation
ty copied to clipboard

support `typing.Self` used as attribute (or dataclass field) type

Open JoanPuig opened this issue 4 months ago • 3 comments

Summary

The following code:

from dataclasses import dataclass
from typing import Optional, Self


@dataclass(frozen=True)
class MyClass:
    field: Optional[Self] = None


MyClass(MyClass())

reports the following, which I believe should not be an error:

error[invalid-argument-type]: Argument is incorrect
  --> src\main.py:10:9
   |
10 | MyClass(MyClass())
   |         ^^^^^^^^^ Expected `typing.Self | None`, found `MyClass`
   |
info: rule `invalid-argument-type` is enabled by default

Found 1 diagnostic

Version

0.0.1-alpha.20 (f41f00af1 2025-09-03)

JoanPuig avatar Sep 03 '25 20:09 JoanPuig

There's also this dataclass-less version, which is a bit different in that it doesn't involve a synthesized __init__ method, and which we also wrongly error on:

from typing import Optional, Self


class MyClass:
    field: Optional[Self] = None


def _(c: MyClass):
    c.field = c

carljm avatar Sep 03 '25 20:09 carljm

Thanks for the report!

carljm avatar Sep 03 '25 20:09 carljm

Another interesting example which does not involve any assignments. I believe it is a special case of this issue, since the diagnostic only occurs if the Self type is used in a field.

from typing import Generic, TypeVar, Self


class Fruit:
    consumer: "Consumer[Self] | None" = None


T = TypeVar("T", bound=Fruit)


class Consumer(Generic[T]): ...

Ty output:

error[invalid-type-arguments]: Type `<special-form 'typing.Self'>` is not assignable to upper bound `Fruit` of type variable `T@Consumer`
 --> self_bound.py:5:25
  |
4 | class Fruit:
5 |     consumer: "Consumer[Self] | None" = None
  |                         ^^^^
  |
 ::: self_bound.py:8:1
  |
8 | T = TypeVar("T", bound=Fruit)
  | - Type variable defined here
  |
info: rule `invalid-type-arguments` is enabled by default

Wizzerinus avatar Dec 26 '25 09:12 Wizzerinus