Question Details

No question body available.

Tags

python python-typing python-dataclasses

Answers (1)

Accepted Answer Available
Accepted Answer
January 25, 2026 Score: 3 Rep: 42,360 Quality: Expert Completeness: 80%

Dataclasses and fields have special handling by type checkers. That is, they are not treated like regular source code. Instead, the type checker knows that a transformation is taking place, and what actually appears in the code source isn't directly how the code will be used. Rather, it is metadata that specifies how that transformation takes place.

The complexity surrounding the transformation logic means most (all?) type checkers only apply this logic to known symbols of the dataclasses module. However, to help with customisation of dataclasses, PEP 681 introduced an extra symbol that lets a type checker know a dataclass transformation is being performed: typing.dataclasstransform (available from Python 3.11, and backported to older versions of Python in typingextensions from version 4.6). It is used as a decorator to tell the type checker that the decorated object performs the same transformation that dataclasses.dataclass does. It also comes with a keyword fieldspecifiers that lets the type checker know what other types and functions can be treated the same as the field function. For example:

from typing import dataclasstransform, Any
from dataclasses import dataclass, field

despite all these Any types, the type checker will know that

customfield(init=False) describes a field that is present on instance of the

dataclass, but should not be part of the init of the object.

def customfield(*args: Any, kwargs: Any) -> Any: # NB. customfield may not alias or rename its arguments. The type checker will # not understand. You must use the same names and types for arguments return field(args, kwargs, metadata={'example_metadata': 42})

this decorator tells the type checker that dataclass_with_metadata applies

a dataclass transformation, but that custom_field can also be used to mark

a name as a field.

@dataclass_transform(field_specifiers=(custom_field,)) def dataclass_with_metadata[T](cls: type[T]) -> type[T]: return dataclass(cls)

@dataclass_with_metadata class CustomDataClass(): a : str b : int c : int = custom_field(init=False)

value = CustomDataClass(a='a', b=1) # type checks okay

However, whilst the type checker is able to correctly infer what custom_field(init=False) means in this context, the use of Any means the type checker will not be able to check that the use of custom_field itself is correct. For instance, if you mispell init as onit. If you wanted a type safe custom_field then you can use a ParamSpec to show that custom_field has the same signature as field. For example:

from typing import Callable

def custom_field_creator[P, R](func: Callable[P, R]) -> Callable[P, R]: def wrapper(
args: P.args, kwargs: P.kwargs) -> R: kwargs.setdefault('metadata', {'examplemetadata': 42}) return func(*args, **kwargs) return wrapper customfield = customfieldcreator(field)