You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Frozen dataclasses should have an implicit Final annotation on its members #20087
Frozen dataclasses should be implicitly Final on its members.
Currently mypy detects correctly that a frozen dataclass member cannot be assigned to; this is similar (if not altogether equivalent) to having a Final annotation on that (and every) member. This is important because it affects type covariance:
To Reproduce
fromtypingimportcastfromdataclassesimportdataclassclassC: passclassD(C): pass@dataclass(frozen=True)classMyData[T: C]:
m: Tdeff(x: MyData[C]) ->None: passf(cast(MyData[D], 0)) # error, T is invariant here because `m` is public and deemed assignable
However, since MyData above is frozen, m cannot be assigned to and can be deemed final for purposes of covariance.
Expected Behavior
The above should typecheck:
f(cast(MyData[D], 0)) # this should typecheck
Actual Behavior
The above doesn't typecheck.
Your Environment
Mypy version used: 1.18.2
Mypy command-line flags: nothing special
Mypy configuration options from mypy.ini (and other config files): strict mode = True
The current workaround is to use an explicit TypeVar with covariance=True in cases where covariance of this kind is desirable. There are no consequences other than having uglier code. Also, note that currently there is no way to explicitly annotate members as Final, because mypy does not accept typevars as arguments to Final.
Activity
alecov commentedon Oct 18, 2025
The current workaround is to use an explicit
TypeVarwithcovariance=Truein cases where covariance of this kind is desirable. There are no consequences other than having uglier code. Also, note that currently there is no way to explicitly annotate members asFinal, because mypy does not accept typevars as arguments toFinal.erictraut commentedon Oct 19, 2025
This issue is discussed here. The invariance is due to the synthesized
__replace__method, which was introduced in Python 3.13.alecov commentedon Oct 20, 2025
Good, I think we can close this as a duplicate, then.