python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.19k stars 2.78k forks source link

dataclasses: optional generic fields with non-None defaults #15887

Open eltoder opened 1 year ago

eltoder commented 1 year ago

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.11&gist=bcf4de945268517a0cd8b6b9835e852f

from dataclasses import dataclass, field
from typing import Generic, TypeVar

T = TypeVar("T")

class V(Generic[T]):
    def __init__(self, *v: T) -> None:
        pass

class C:
    x: dict[str, int] | None = field(default_factory=dict)
    y: V[int] | None = field(default=V())

Expected Behavior

This should produce no errors.

Actual Behavior

main.py:11: error: Incompatible types in assignment (expression has type "dict[_KT, _VT]", variable has type "dict[str, int] | None")  [assignment]
main.py:12: error: Incompatible types in assignment (expression has type "V[<nothing>]", variable has type "V[int] | None")  [assignment]
Found 2 errors in 1 file (checked 1 source file)

If I remove | None, mypy works as expected. However, AFAICT, outside of dataclasses such assignments are accepted.

Your Environment

eltoder commented 1 year ago

Also, if I change y to use direct assignment instead of using field, the second error disappears:

class C:
    x: dict[str, int] | None = field(default_factory=dict)
    y: V[int] | None = V()  # no error this way
erictraut commented 1 year ago

Here's a workaround that type checks without errors:

class C:
    x: dict[str, int] | None = field(default_factory=dict[str, int])
    y: V[int] | None = field(default=V[int]())