Also, a note that in Python 3.10, you can also pass the kw_only parameter to the @dataclass decorator to work around the issue which I suspect you're having, wherein all fields in a subclass are required to have a default value when there is at least one field with a default value in the superclass, Mixin in this case.
I added an example below to illustrate this a little better:
from dataclasses import dataclass
@dataclass
class Mixin:
string: str
integer: int = 222
@dataclass(kw_only=True)
class User(Mixin):
id: int
items: list['A | B | C']
class A: ...
class B: ...
class C: ...
u = User(string='abc', id=321, integer=123, items=[])
print(u)
Note that I've also wrapped the Union arguments in a string, so that the expression is forward-declared (i.e. not evaluated yet), since the classes in the Union arguments are defined a bit later.
This code works in 3.10 because the kw_only param is enabled, so now only keyword arguments are accepted to the constructor. This allows you to work around that issue as mentioned, where you would otherwise need to define a default value for all fields in a subclass when there's at least one default field in a parent class.
In earlier Python versions than 3.10, missing the kw_only argument, you'd expect to run into a TypeError as below:
TypeError: non-default argument 'id' follows default argument
The workaround for this in a pre-3.10 scenario is exactly how you had it: define a default value for all fields in the User class as below.
from __future__ import annotations
from dataclasses import dataclass, field
@dataclass
class Mixin:
string: str
integer: int = 222
@dataclass
class User(Mixin):
id: int = None
items: list[A | B | C] = field(default_factory=list)
class A: ...
class B: ...
class C: ...
u = User('abc', 123, 321)
print(u)