Another option that some libraries take when extending dataclass is to make Dataclass a plain base class. This has a number of benefits over the decorator approach, including isinstance checking, and the ability to add methods and class attributes. You can use a plain base class thanks to __init_subclass__.
I always thought the decorator approach was chosen over base classes because dataclasses was borrowed from attrs?
I think using isinstance is an anti-pattern. Why would you need to know if two unrelated classes are dataclasses? It’s like namedtuple: there’s no common base class for a reason.
For ordinary dataclasses, it would come in handy since it would allow you to properly type annotate and type check dataclasses.replace and dataclasses.fields.
I also have written functions that accept a dataclass and iterate over the fields. So I would like to annotate such a function properly and do type checking within that function. isinstance is the canonical way to check a type.
For specialized dataclass, there’s even more reason to want to check. For example, flax.struct.dataclass is a specialized dataclass used in Flax. Many functions only accept “PyTrees” (a concept in Jax), which is basically most scalar types and flax.struct.dataclass. So it would really help to be able to check.
Specialized dataclasses can add behavior, so I think it makes sense to want to check if an object exposes that behavior.
At the risk of straying further OT the attrs plug-in for mypy “injects” an attrs-specific dunder to attrs classes. The dunder’s protocol allows typing users to benefit from the same typing guarantees that subclassing would provide. The same I imagine would hold true for dataclasses.