felangel / equatable

A Dart package that helps to implement value based equality without needing to explicitly override == and hashCode.
https://pub.dev/packages/equatable
MIT License
901 stars 100 forks source link

Feat: Add derived object comparability #133

Closed mrgnhnt96 closed 9 months ago

mrgnhnt96 commented 2 years ago

Status

IN DEVELOPMENT

Breaking Changes

NO

Description

When comparing derived (sub) classes to it's base class, the result it always false. I would expect that, if the base props matches the derived class's base props, the result would be true.

Todos

Steps to Test or Reproduce

Outline the steps to test or reproduce the PR here.

import 'package:equatable/equatable.dart';

class A extends Equatable {
  const A(this.value);
  final String value;

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

class B extends A {
  const B(String a, this.code) : super(a);
  final int code;

  @override
  List<Object?> get props => [...super.props, code];

  @override
  Set<Type> get derived => {A};
}

class C extends B {
  const C(
    String value,
    int code, {
    required this.isCool,
  }) : super(value, code);

  final bool isCool;

  @override
  Set<Type> get derived => {B, ...super.derived};

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

void main() {
  final notEqualA = const A('not-equal');

  final instanceA = const A('a');
  final instanceB = const B('a', 1);
  final instanceC = const C('a', 1, isCool: true);

  print(notEqualA == instanceA); // false

  print(instanceA == instanceB); // true
  print(instanceA == instanceC); // true

  print(instanceB == instanceA); // true
  print(instanceB == instanceC); // true

  print(instanceC == instanceA); // true
  print(instanceC == instanceB); // true

  // reversed
  print(instanceB == instanceA); // true
  print(instanceC == instanceA); // true

  print(instanceA == instanceB); // true
  print(instanceC == instanceB); // true

  print(instanceA == instanceC); // true
  print(instanceB == instanceC); // true
}

Impact to Remaining Code Base

This PR will affect:

Possible Concerns

By manipulating the == to match derivatives from the base class, the hashCode will not return the same result as the == operator. This only occurs when derived is overridden, otherwise the results ARE the same.

HashCode Docs

/*
Base:        instanceA
Derivative:  instanceB
*/

print(instanceA == instanceB); // true
print(instanceA.hashCode == instanceB.hashCode); // false
karimkod commented 2 years ago

I think it would be a better idea to add a configuration (like we have for stringify) to include runtimeType in equality instead of "derived".

felangel commented 9 months ago

Closing this for now but happy to continue discussing this via an issue or on discord.