Skip to content

Add MappableBase<TSelf> interface to guarantee toMap, toJson, and copyWith across generated models#317

Open
okandemirofficial wants to merge 2 commits into
schultek:mainfrom
okandemirofficial:feat/mappable_base
Open

Add MappableBase<TSelf> interface to guarantee toMap, toJson, and copyWith across generated models#317
okandemirofficial wants to merge 2 commits into
schultek:mainfrom
okandemirofficial:feat/mappable_base

Conversation

@okandemirofficial
Copy link
Copy Markdown

@okandemirofficial okandemirofficial commented Oct 8, 2025

Summary

This PR introduces a small, self-referential generic interface MappableBase<TSelf extends MappableBase>.
All generated model mixins now implement MappableBase, so generic services can rely on toMap(), toJson(), and copyWith without extra flags.

Why

I often write generic helpers/services that should work with any mappable type:

class MyService<T extends MappableBase> {
  void convertToJson(T value) {
    final json = value.toMap();
  }
}

Having a shared capability interface lets the compiler enforce these features at call sites.

What changed

Added base mixin MappableBase

mixin MappableBase<T> {
  String toJson();
  Map<String, dynamic> toMap();

  @override
  String toString();

  @override
  bool operator ==(Object other);

  @override
  int get hashCode;

  ClassCopyWith<T, T, T> get copyWith;
}

Updated the generator to emit:

mixin MyModelMappable implements MappableBase {

If this approach doesn’t make sense or there’s a better way to achieve it, please feel free to let me know and disregard the PR.

I really appreciate your work — thanks in advance for taking the time to review this!

@okandemirofficial okandemirofficial changed the title Mappable Base Implementation Add MappableBase<TSelf> interface to guarantee toMap, toJson, and copyWith across generated models Oct 8, 2025
@zajrik
Copy link
Copy Markdown
Contributor

zajrik commented Feb 25, 2026

I checked this out because it's an addition I would benefit from but I've discovered some problems. First, it assumes that the user will never use anything less than the full suite of generated methods, so I had a few errors here and there from my mappable classes for which I don't bother with serialization:

@MappableClass(generateMethods: GenerateMethods.copy | GenerateMethods.equals)
class SessionState with SessionStateMappable
// Errors for missing toMap/toJson implementations

Second, it breaks with inheritance. With this base class:

@MappableClass(
  discriminatorKey: 'kind',
  includeSubClasses: [SingleTagCondition, MultiTagCondition, TokenStatCondition],
)
abstract base class EffectCondition with EffectConditionMappable

all of its sub-classes error because they try to implement both MappableBase<self> and MappableBase<EffectCondition>, self of course representing any of the sub-classes.

This is an excellent idea but in practice I'm not sure how it will work. The first issue can't really be addressed without requiring the user to generate all mappable methods for all mappable classes, and I'm not sure about the second issue either, but there may be some generation magic involving the included subclasses that could be done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants