felangel/equatable

Comparing Derived Classes Fails When Shared Properties Are The Same

SupposedlySam opened this issue · 4 comments

Describe the bug
When passing in a derived class (subclass) and doing an equality comparison, the comparison fails.

To Reproduce

import 'package:equatable/equatable.dart';

void main() {
  const String sameValue = 'shouldBeEqual';

  final c = C(A(sameValue), B(sameValue, 'someOtherValue'));

  print(c.compare); // false
}

class A extends Equatable {
  const A(this.sameValue);

  final String sameValue;

  @override
  List<Object> get props => [sameValue];
}

class B extends A {
  const B(String sameValue, this.differentValue) : super(sameValue);

  final String differentValue;

  @override
  List<Object> get props => [differentValue, ...super.props];
}

class C {
  const C(this.firstClass, this.secondClass);

  final A firstClass;
  final A secondClass;

  bool get compare => firstClass == secondClass;
}

Expected behavior
Since class C is requiring two classes of type A, I would expect for c.compare to return true since a.sameValue and b.sameValue are equal between the two instances.

Version
Equatable: ^2.0.3
Dart SDK version: 2.15.0 (stable) (Fri Dec 3 14:23:23 2021 +0100) on "macos_x64"

Hey @SupposedlySam 👋
Thanks for opening an issue!

I believe this is a duplicate of #131. I left a comment with my thoughts but lmk what you think 👍

If you want to achieve this effect you should be able to create a generic mixin like:

import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';

abstract class Animal extends Equatable {
  const Animal({required this.name});
  final String name;

  @override
  List<Object> get props => [name];
}

mixin EqualityMixin<T extends Equatable> on Equatable {
  static const _equality = DeepCollectionEquality();

  @override
  bool operator ==(Object other) {
    return other is T && _equality.equals(props, other.props);
  }
}

class Cat extends Animal with EqualityMixin<Animal> {
  const Cat({required String name}) : super(name: name);
}

class Dog extends Animal with EqualityMixin<Animal> {
  const Dog({required String name}) : super(name: name);
}

void main() {
  final cat = Cat(name: 'Taco');
  final dog = Dog(name: 'Taco');

  print(cat == dog);
}

I've worked with Morgan on a proposal. Linking the draft PR.

Here is a possible solution for this #133

Closing as dup of #131. Conversation continued there.