dart_mappable icon indicating copy to clipboard operation
dart_mappable copied to clipboard

Generated copyWith looses type information if nested in another class

Open nblum37 opened this issue 3 years ago • 7 comments

Hi :) I have the following (very strange) issue as an extension to the environment in #24. Image the following class definition:

@immutable
@MappableClass()
class SingleSetting<T> with Mappable {
  final List<T>? properties;

  const SingleSetting({
    this.properties,
  });
}

@immutable
@MappableClass()
class Settings with Mappable {
  final Map<String, SingleSetting>? settings;

  const Settings({
   this.settings,
  });
}

If I initiate something like SingleSetting setting = SingleSetting<String>(); the condition setting.copyWith(properties: ['test']).runtimeType == SingleSetting<String> is true.

If I prepare the following class Settings settings = Settings<String>; settings = settings.copyWith(settings: {'Test': const SingleSetting<String>(properties: ['test'])}); SingleSetting setting = settings.settings['Test']; the condition setting.runtimeType == SingleSetting<String> is true as expected. However the condition setting.copyWith(properties: ['test']).runtimeType == SingleSetting<String> is false but setting.copyWith(properties: ['test']).runtimeType == SingleSetting<dynamic> is true.

The behavior is very strange and for now, I can't explain it. Do you have any ideas, about what the issue can be? Thank you!

nblum37 avatar Oct 27 '22 20:10 nblum37

Ok makes sense. Its because for this class the copyWith is still an extension and not a mixin, which causes the type parameter to get lost. So I just change it to mixins everywhere.

schultek avatar Oct 27 '22 21:10 schultek

Ok, good, that it somehow makes sense. I was already worried to write a complete stupid GIT issue :D

nblum37 avatar Oct 27 '22 21:10 nblum37

If you are interested why this is: Extensions work with the static type of an object, while mixins work with the runtime type.

So having an extension on A<T>, then for A<dynamic> a = A<int>() the extension would only get T = dynamic while the same as a mixin would have the correct type T = int.

I just didn't realize this before.

schultek avatar Oct 27 '22 22:10 schultek

Thanks for the explanation! I wasn't aware of that, as well.

nblum37 avatar Oct 28 '22 08:10 nblum37

Published in next prerelease

schultek avatar Oct 28 '22 13:10 schultek

Hi :) Unfortunately, the issue still exists in dev.5. Now with:

@immutable
@MappableClass()
class SingleSetting<T> with SingleSettingMappable<T> {
  final List<T>? properties;

  const SingleSetting({
    this.properties,
  });
}

@immutable
@MappableClass()
class Settings with SettingsMappable {
  final Map<String, SingleSetting>? settings;

  const Settings({
   this.settings,
  });
}

nblum37 avatar Nov 02 '22 03:11 nblum37

Do you have some reproduction code?

schultek avatar Nov 02 '22 10:11 schultek

@nblum37 any updates?

schultek avatar Nov 15 '22 14:11 schultek

I am unfortunately currently finishing our next release. I hope I will be able to provide the reproduction code next week.

nblum37 avatar Nov 16 '22 16:11 nblum37

I will close this now. If the issue persists in the newest version (2.0.0-dev.8) please reopen.

schultek avatar Jan 02 '23 20:01 schultek

I will check it with the new dev version :)

nblum37 avatar Jan 03 '23 22:01 nblum37