Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidType generated when using Color as a return type in a function proeprty #959

Open
opsb opened this issue Jul 30, 2023 · 16 comments
Open
Assignees
Labels
bug Something isn't working question Further information is requested

Comments

@opsb
Copy link

opsb commented Jul 30, 2023

Describe the bug
Given the following versions:

  • freezed 2.4.1
  • freezed_annotation 2.4.1
  • flutter 3.10.6
  • dart 3.0.7

When freezed generates the following definition it will use InvalidType as the return type for the color() function.

@freezed
class TShirt {
  factory TShirt({
	required Color color(),
  }) = _TShirt;
}

If it's changed to be just a property:

@freezed
class TShirt {
  factory TShirt({
	required Color color,
  }) = _TShirt;
}

Then it generates correctly with the Color type.

To Reproduce
Run the generator on the following

@freezed
class TShirt {
  factory TShirt({
	required Color color(),
  }) = _TShirt;
}

check the output and see InvalidType in the generated .freezed file.

Expected behavior
The .freezed file should use Color instead of InvalidType

Workaround
In a pinch you can use a wrapper class

class ColorWrapper {
	  final Color color;

  ColorWrapper(this.color);
}

@freezed
class TShirt {
  factory TShirt({
	required ColorWrapper color(),
  }) = _TShirt;
}

Which is a little inconvenient to use but does generate the correct types.

Edit: Have also discovered the same issue when using https://api.flutter.dev/flutter/dart-ui/Offset-class.html as a simple property.

@freezed
class Location {
  factory Location({
	required Offset offset,
  }) = _Location;
}
@opsb opsb added bug Something isn't working needs triage labels Jul 30, 2023
@rrousselGit
Copy link
Owner

Cf #954

This should work. Try running dart pub upgrade

@opsb
Copy link
Author

opsb commented Jul 31, 2023

I had tried but I ran it again for good measure. The lockfile says 2.4.1 for both freezed_annotation and freezed. When I run build_runner I still end up with InvalidType.

@rrousselGit rrousselGit reopened this Jul 31, 2023
@rrousselGit
Copy link
Owner

It works just fine for me:

Screenshot 2023-07-31 at 14 08 56

Note that in your snippets, you forgot to add the _$TShirt mixin.

@opsb
Copy link
Author

opsb commented Aug 3, 2023

Note that in your snippets, you forgot to add the _$TShirt mixin.

Sorry yes that was just an omission from the example code, it's there in the app code. I wasn't able to clear the issue but in this case it made sense to switch to a sealed class anyway.

@rrousselGit rrousselGit added question Further information is requested and removed needs triage labels Aug 3, 2023
@rrousselGit
Copy link
Owner

Still, it runs just fine for me.
Maybe try making a git repository to reproduce the error.

Otherwise I would lean toward closing this as there's nothing I can do and it seems to work.

@SEGVeenstra
Copy link

I seem to have run into a similar issue where freezed somehow generates InvalidType.

It's in the following class, where ShCreationAssignmentMultipleChoice is also a feezed object.

part 'multiple_choice_detail_state.freezed.dart';

@freezed
sealed class MultipleChoiceDetailState with _$MultipleChoiceDetailState {
  const factory MultipleChoiceDetailState.editing({
    required ShCreationAssignmentMultipleChoice savedMultipleChoice,
    required ShCreationAssignmentMultipleChoice multipleChoice,
  }) = MultipleChoiceDetailEditing;

  const factory MultipleChoiceDetailState.saved({
    required ShCreationAssignmentMultipleChoice multipleChoice,
  }) = MultipleChoiceDetailSaved;
}
@freezed
sealed class ShCreationAssignment with _$ShCreationAssignment {
  const factory ShCreationAssignment.multipleChoice({
    required String question,
    required String correctAnswer,
    required List<String> answers,
  }) = ShCreationAssignmentMultipleChoice;
}

This will generate the following:

Code with InvalidType like:
InvalidType get multipleChoice => throw _privateConstructorUsedError;

collapse for full code

// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark

part of 'multiple_choice_detail_state.dart';

// **************************************************************************
// FreezedGenerator
// **************************************************************************

T _$identity<T>(T value) => value;

final _privateConstructorUsedError = UnsupportedError(
  'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');

/// @nodoc
mixin _$MultipleChoiceDetailState {
InvalidType get multipleChoice => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
  required TResult Function(
          ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)
      editing,
  required TResult Function(ShCreationAssignmentMultipleChoice multipleChoice)
      saved,
}) =>
    throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
  TResult? Function(ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)?
      editing,
  TResult? Function(ShCreationAssignmentMultipleChoice multipleChoice)? saved,
}) =>
    throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
  TResult Function(ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)?
      editing,
  TResult Function(ShCreationAssignmentMultipleChoice multipleChoice)? saved,
  required TResult orElse(),
}) =>
    throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
  required TResult Function(MultipleChoiceDetailEditing value) editing,
  required TResult Function(MultipleChoiceDetailSaved value) saved,
}) =>
    throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
  TResult? Function(MultipleChoiceDetailEditing value)? editing,
  TResult? Function(MultipleChoiceDetailSaved value)? saved,
}) =>
    throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
  TResult Function(MultipleChoiceDetailEditing value)? editing,
  TResult Function(MultipleChoiceDetailSaved value)? saved,
  required TResult orElse(),
}) =>
    throw _privateConstructorUsedError;

@JsonKey(ignore: true)
$MultipleChoiceDetailStateCopyWith<MultipleChoiceDetailState> get copyWith =>
    throw _privateConstructorUsedError;
}

/// @nodoc
abstract class $MultipleChoiceDetailStateCopyWith<$Res> {
factory $MultipleChoiceDetailStateCopyWith(MultipleChoiceDetailState value,
        $Res Function(MultipleChoiceDetailState) then) =
    _$MultipleChoiceDetailStateCopyWithImpl<$Res, MultipleChoiceDetailState>;
@useResult
$Res call({InvalidType multipleChoice});
}

/// @nodoc
class _$MultipleChoiceDetailStateCopyWithImpl<$Res,
      $Val extends MultipleChoiceDetailState>
  implements $MultipleChoiceDetailStateCopyWith<$Res> {
_$MultipleChoiceDetailStateCopyWithImpl(this._value, this._then);

// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;

@pragma('vm:prefer-inline')
@override
$Res call({
  Object? multipleChoice = freezed,
}) {
  return _then(_value.copyWith(
    multipleChoice: freezed == multipleChoice
        ? _value.multipleChoice
        : multipleChoice // ignore: cast_nullable_to_non_nullable
            as InvalidType,
  ) as $Val);
}
}

/// @nodoc
abstract class _$$MultipleChoiceDetailEditingCopyWith<$Res>
  implements $MultipleChoiceDetailStateCopyWith<$Res> {
factory _$$MultipleChoiceDetailEditingCopyWith(
        _$MultipleChoiceDetailEditing value,
        $Res Function(_$MultipleChoiceDetailEditing) then) =
    __$$MultipleChoiceDetailEditingCopyWithImpl<$Res>;
@override
@useResult
$Res call(
    {ShCreationAssignmentMultipleChoice savedMultipleChoice,
    ShCreationAssignmentMultipleChoice multipleChoice});
}

/// @nodoc
class __$$MultipleChoiceDetailEditingCopyWithImpl<$Res>
  extends _$MultipleChoiceDetailStateCopyWithImpl<$Res,
      _$MultipleChoiceDetailEditing>
  implements _$$MultipleChoiceDetailEditingCopyWith<$Res> {
__$$MultipleChoiceDetailEditingCopyWithImpl(
    _$MultipleChoiceDetailEditing _value,
    $Res Function(_$MultipleChoiceDetailEditing) _then)
    : super(_value, _then);

@pragma('vm:prefer-inline')
@override
$Res call({
  Object? savedMultipleChoice = freezed,
  Object? multipleChoice = freezed,
}) {
  return _then(_$MultipleChoiceDetailEditing(
    savedMultipleChoice: freezed == savedMultipleChoice
        ? _value.savedMultipleChoice
        : savedMultipleChoice // ignore: cast_nullable_to_non_nullable
            as ShCreationAssignmentMultipleChoice,
    multipleChoice: freezed == multipleChoice
        ? _value.multipleChoice
        : multipleChoice // ignore: cast_nullable_to_non_nullable
            as ShCreationAssignmentMultipleChoice,
  ));
}
}

/// @nodoc

class _$MultipleChoiceDetailEditing implements MultipleChoiceDetailEditing {
const _$MultipleChoiceDetailEditing(
    {required this.savedMultipleChoice, required this.multipleChoice});

@override
final ShCreationAssignmentMultipleChoice savedMultipleChoice;
@override
final ShCreationAssignmentMultipleChoice multipleChoice;

@override
String toString() {
  return 'MultipleChoiceDetailState.editing(savedMultipleChoice: $savedMultipleChoice, multipleChoice: $multipleChoice)';
}

@override
bool operator ==(dynamic other) {
  return identical(this, other) ||
      (other.runtimeType == runtimeType &&
          other is _$MultipleChoiceDetailEditing &&
          const DeepCollectionEquality()
              .equals(other.savedMultipleChoice, savedMultipleChoice) &&
          const DeepCollectionEquality()
              .equals(other.multipleChoice, multipleChoice));
}

@override
int get hashCode => Object.hash(
    runtimeType,
    const DeepCollectionEquality().hash(savedMultipleChoice),
    const DeepCollectionEquality().hash(multipleChoice));

@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$MultipleChoiceDetailEditingCopyWith<_$MultipleChoiceDetailEditing>
    get copyWith => __$$MultipleChoiceDetailEditingCopyWithImpl<
        _$MultipleChoiceDetailEditing>(this, _$identity);

@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
  required TResult Function(
          ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)
      editing,
  required TResult Function(ShCreationAssignmentMultipleChoice multipleChoice)
      saved,
}) {
  return editing(savedMultipleChoice, multipleChoice);
}

@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
  TResult? Function(ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)?
      editing,
  TResult? Function(ShCreationAssignmentMultipleChoice multipleChoice)? saved,
}) {
  return editing?.call(savedMultipleChoice, multipleChoice);
}

@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
  TResult Function(ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)?
      editing,
  TResult Function(ShCreationAssignmentMultipleChoice multipleChoice)? saved,
  required TResult orElse(),
}) {
  if (editing != null) {
    return editing(savedMultipleChoice, multipleChoice);
  }
  return orElse();
}

@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
  required TResult Function(MultipleChoiceDetailEditing value) editing,
  required TResult Function(MultipleChoiceDetailSaved value) saved,
}) {
  return editing(this);
}

@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
  TResult? Function(MultipleChoiceDetailEditing value)? editing,
  TResult? Function(MultipleChoiceDetailSaved value)? saved,
}) {
  return editing?.call(this);
}

@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
  TResult Function(MultipleChoiceDetailEditing value)? editing,
  TResult Function(MultipleChoiceDetailSaved value)? saved,
  required TResult orElse(),
}) {
  if (editing != null) {
    return editing(this);
  }
  return orElse();
}
}

abstract class MultipleChoiceDetailEditing
  implements MultipleChoiceDetailState {
const factory MultipleChoiceDetailEditing(
    {required final ShCreationAssignmentMultipleChoice savedMultipleChoice,
    required final ShCreationAssignmentMultipleChoice
        multipleChoice}) = _$MultipleChoiceDetailEditing;

ShCreationAssignmentMultipleChoice get savedMultipleChoice;
@override
ShCreationAssignmentMultipleChoice get multipleChoice;
@override
@JsonKey(ignore: true)
_$$MultipleChoiceDetailEditingCopyWith<_$MultipleChoiceDetailEditing>
    get copyWith => throw _privateConstructorUsedError;
}

/// @nodoc
abstract class _$$MultipleChoiceDetailSavedCopyWith<$Res>
  implements $MultipleChoiceDetailStateCopyWith<$Res> {
factory _$$MultipleChoiceDetailSavedCopyWith(
        _$MultipleChoiceDetailSaved value,
        $Res Function(_$MultipleChoiceDetailSaved) then) =
    __$$MultipleChoiceDetailSavedCopyWithImpl<$Res>;
@override
@useResult
$Res call({ShCreationAssignmentMultipleChoice multipleChoice});
}

/// @nodoc
class __$$MultipleChoiceDetailSavedCopyWithImpl<$Res>
  extends _$MultipleChoiceDetailStateCopyWithImpl<$Res,
      _$MultipleChoiceDetailSaved>
  implements _$$MultipleChoiceDetailSavedCopyWith<$Res> {
__$$MultipleChoiceDetailSavedCopyWithImpl(_$MultipleChoiceDetailSaved _value,
    $Res Function(_$MultipleChoiceDetailSaved) _then)
    : super(_value, _then);

@pragma('vm:prefer-inline')
@override
$Res call({
  Object? multipleChoice = freezed,
}) {
  return _then(_$MultipleChoiceDetailSaved(
    multipleChoice: freezed == multipleChoice
        ? _value.multipleChoice
        : multipleChoice // ignore: cast_nullable_to_non_nullable
            as ShCreationAssignmentMultipleChoice,
  ));
}
}

/// @nodoc

class _$MultipleChoiceDetailSaved implements MultipleChoiceDetailSaved {
const _$MultipleChoiceDetailSaved({required this.multipleChoice});

@override
final ShCreationAssignmentMultipleChoice multipleChoice;

@override
String toString() {
  return 'MultipleChoiceDetailState.saved(multipleChoice: $multipleChoice)';
}

@override
bool operator ==(dynamic other) {
  return identical(this, other) ||
      (other.runtimeType == runtimeType &&
          other is _$MultipleChoiceDetailSaved &&
          const DeepCollectionEquality()
              .equals(other.multipleChoice, multipleChoice));
}

@override
int get hashCode => Object.hash(
    runtimeType, const DeepCollectionEquality().hash(multipleChoice));

@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$MultipleChoiceDetailSavedCopyWith<_$MultipleChoiceDetailSaved>
    get copyWith => __$$MultipleChoiceDetailSavedCopyWithImpl<
        _$MultipleChoiceDetailSaved>(this, _$identity);

@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
  required TResult Function(
          ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)
      editing,
  required TResult Function(ShCreationAssignmentMultipleChoice multipleChoice)
      saved,
}) {
  return saved(multipleChoice);
}

@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
  TResult? Function(ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)?
      editing,
  TResult? Function(ShCreationAssignmentMultipleChoice multipleChoice)? saved,
}) {
  return saved?.call(multipleChoice);
}

@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
  TResult Function(ShCreationAssignmentMultipleChoice savedMultipleChoice,
          ShCreationAssignmentMultipleChoice multipleChoice)?
      editing,
  TResult Function(ShCreationAssignmentMultipleChoice multipleChoice)? saved,
  required TResult orElse(),
}) {
  if (saved != null) {
    return saved(multipleChoice);
  }
  return orElse();
}

@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
  required TResult Function(MultipleChoiceDetailEditing value) editing,
  required TResult Function(MultipleChoiceDetailSaved value) saved,
}) {
  return saved(this);
}

@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
  TResult? Function(MultipleChoiceDetailEditing value)? editing,
  TResult? Function(MultipleChoiceDetailSaved value)? saved,
}) {
  return saved?.call(this);
}

@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
  TResult Function(MultipleChoiceDetailEditing value)? editing,
  TResult Function(MultipleChoiceDetailSaved value)? saved,
  required TResult orElse(),
}) {
  if (saved != null) {
    return saved(this);
  }
  return orElse();
}
}

abstract class MultipleChoiceDetailSaved implements MultipleChoiceDetailState {
const factory MultipleChoiceDetailSaved(
        {required final ShCreationAssignmentMultipleChoice multipleChoice}) =
    _$MultipleChoiceDetailSaved;

@override
ShCreationAssignmentMultipleChoice get multipleChoice;
@override
@JsonKey(ignore: true)
_$$MultipleChoiceDetailSavedCopyWith<_$MultipleChoiceDetailSaved>
    get copyWith => throw _privateConstructorUsedError;
}

@SEGVeenstra
Copy link

After some more Googling, I stumbled upon this comment:

Analyzer recently changed to give an InvalidType instead of dynamic when it can't resolve a type, and freezed may need to be updated accordingly.
dart-lang/build#3527 (comment)

Could this be the problem here?
I'm currently not yet running the latest version of freezed as some dependancies won't allow me to update.
Is it fixed/resolved in the later freezed versions?

If so, I'll try to get it working, or else I might need to downgrade maybe?

@SEGVeenstra
Copy link

So I believe for me the issue relates to the fact that I'm using classes generated by freezed as properties for other freezed classes.
This happens to work correctly in some cases, but even when downgrading the analyzer, which causes the InvalidType for me, this still wouldn't work.

Here the problem was that it will try to override the copyWith and because it's using classes that don't really exists before generating them. The second class will use dynamics instead of the correct class names, resulting in a mismatch.

I found on different issues that @rrousselGit has commented about freezed not supporting this, but I couldn't find anything about this in the docs. It might save people a headache if we would add something to the docs?

@SunlightBro
Copy link
Contributor

SunlightBro commented Sep 15, 2023

Its a general incapability of
analyzer => build_runner

so not really freezed specific.
But because no one really checks the README of build_runner, analyzer and source_gen, when they wanna use a code-gen package. It would be good to add some Warning in freezed README.

⚠️ Warning
You can not rely on/use types in your freezed classes, the need to be generated them self.

@SEGVeenstra for more info checkout:
#916

@SEGVeenstra
Copy link

@SunlightBro Yes I figured this much when I found this:
dart-lang/sdk#52455

@rrousselGit
Copy link
Owner

There are plans to deal with this. We can do something about it without macros, but it requires a significant rewrite of Freezed.

There's one in progress (since macros are talking their time :P). But it's not gonna get released soon, as I work on Riverpod first and foremost

@SEGVeenstra
Copy link

Thanks for the response @rrousselGit.

I've worked around this in my project currently by not using freezed for that specific class and just make my own sealed class and use equatable and write my own copyWith.

Will keep my eyes out for this!

@rrousselGit
Copy link
Owner

Note that for those cases, you can make a wrapper if that helps.

You can do:

class Wrapper {
  Wrapper(this.type);
  final GeneratedType type;
}

...
factory MyClass(Wrapper wrapper) = _MyClass;

That won't generate dynamic

@nerder
Copy link

nerder commented May 1, 2024

I'm aware that macros are almost here and this issue will be closed pretty soon, but I found another strange workaround, by just adding an empty not used factory constructor somehow make it work.

@freezed
class AnotherClass with _$AnotherClass {
  const factory AnotherClass.notUsed() = _NotUsed;

  const factory AnotherClass.method({required MyFreezedClass myFreezedClass}) =
      Method;
}

@rrousselGit
Copy link
Owner

That's not really a workaround. The fact is that this issue is a bit flacky due to how analyzer works. Sometimes editing a file makes the issue go away.

@nerder
Copy link

nerder commented May 17, 2024

There are plans to deal with this. We can do something about it without macros, but it requires a significant rewrite of Freezed.

There's one in progress (since macros are talking their time :P). But it's not gonna get released soon, as I work on Riverpod first and foremost

About this, can you share the issue on where this work has been tracked? I'd love to read it and eventually contribute it.

PS: @rrousselGit mark this comment as off-topic pls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants