Freezed: Add ability to ensure correct data

Created on 15 Mar 2020  路  10Comments  路  Source: rrousselGit/freezed

I would like to be able to check the given data, when a freezed object is constructed, somewhat like assert statements in normal constructors.
I propose the following syntax:

@freezed
abstract class Foo with _$Foo {
  @Assert((foo) => foo.value <= maxValue)
  factory Foo({
    @required double value, 
    @Default(double.infinity) double maxValue
  }) = _Foo;
} 

Would something like this be possible?

Alternatively a simpler approach would be an onConstruct callback like this:

@freezed
abstract class Foo with _$Foo {
  factory Foo({
    @required double value, 
    @Default(double.infinity) double maxValue
  }) = _Foo;

  @override
  void onConstruct() {
    assert(value <= maxValue);
  } 
} 

What do you think of this?

enhancement

Most helpful comment

Does this mean, that syntax and other errors will be thrown at compile time (or in this case while the code is generated)?

You will have a compilation error.

All 10 comments

@Assert((foo) => foo.value <= maxValue)

This is not a valid Dart syntax

void onConstruct() {

We would lose const constructors

What about:

@freezed
abstract class Foo with _$Foo {
  @Assert('value <= maxValue')
  factory Foo({
    @required double value, 
    @Default(double.infinity) double maxValue
  }) = _Foo;
} 

?

We lose auto-completion but it's still type-safe, and this allows const constructors.

That would be a simple solution, but not very flexible. You would have to implement some special syntax checking, especially if one wants to check multiple properties (correct me, if I'm wrong).
I would rather accept the fact of loosing const factory constructors. Would it be possible to use the onConstructed callback only if the class factory isn't specified as const?

If that's not possible or wanted, I would also be fine with your proposal.

You would have to implement some special syntax checking, especially if one wants to check multiple properties

You could do:

@freezed
abstract class Foo with _$Foo {
  @Assert('a > 0')
  @Assert('b > 0')
  @Assert('a > b')
  factory Foo(int a, int b) = _Foo;
}

There's no need for special checking of any sort, since the string is used for generation.\
It's not evaluated or anything, where I would have to parse Dart syntax.

It would just transform @Assert('whatever') into assert(whatever)

Oh okay that sounds cool, I didn't know you could do this. I would appreciate this feature in a coming version.
Thank you!

There's no need for special checking of any sort, since the string is used for generation.
It's not evaluated or anything, where I would have to parse Dart syntax.

It would just transform @Assert('whatever') into assert(whatever)

Does this mean, that syntax and other errors will be thrown at compile time (or in this case while the code is generated)?

Does this mean, that syntax and other errors will be thrown at compile time (or in this case while the code is generated)?

You will have a compilation error.

Would it be a more general apportion an option? If it would be possible to write your own factory method.

@freezed
abstract class Foo with _$Foo {
  factory Foo({
    @required double value, 
    @Default(double.infinity) double maxValue
  }) {
    if(value >= maxValue) {
      throw MyCustomException
    }
    return _Foo(value, maxValue);
  }
} 

The benefits on this approach are

  • Dart idiomatic
  • You can throw your Domain-Exceptions and not Freezed-Exception => No dependencies to the lib in your business code where you can handle the exception.

What you do think? Would that be possible?

@swissonid This is not feasible
It would exponentially increase the difficulty of parsing the name of the generated class (here _Foo).

@rrousselGit I also want this functionality. Assume following class:

@freezed
abstract class MyClass<T> implements _$MyClass<T> {
  const MyClass._();
  factory MyRepo(T a) = _MyClass<T>;
}

Here, I want to check whether the class was instantiated WITH generic type argument given.
That means,

MyClass<int> m = MyClass(5);

should be allowed.

But,

MyClass m = MyClass(5);

shouldn't be.

For this, I want to put an assert statement like this is resultant class:

assert(T != dynamic);
Was this page helpful?
0 / 5 - 0 ratings