Skip to content

Awkward two-constructor necessity in extension type with validation #3343

Open
@srawlins

Description

@srawlins

IIUC, when I want to validate a representation object, I can do that in a constructor in an extension type. But a primary constructor doesn't have a body, so that must be done as a separate constructor. I must declare two constructors then, and one of them must not be usable. Here's an example:

extension type DegreesKelvin._(double degrees) {
  DegreesKelvin(this.degrees) {
    if (degrees < 0) throw ArgumentError('must be positive');
  }
}
  • It feels awkward that a very common use case (I would think), validation, would require this extra dance. I must declare the primary constructor because that's how I specify the representation, and I must declare another constructor which includes validation.
  • Making the primary constructor private doesn't protect call sites within this library. Callers can still call DegreesKelvin._ and side-step the validation.

Possible solutions

Encourage validation through isValid instance methods

This is how the example in the spec is written.

Declare primary constructor in body

@bwilkerson suggested a syntax like:

extension type DegreesKelvin {
  primary DegreesKelvin(int degrees) {
    if (degrees < 0) throw ArgumentError('must be positive');
  }
}

The modifier primary is, at this point, only a signal for the compiler to see which constructor declares the representation type and representation field name.

No primary constructors; go back to declaring the single field explicitly, as in inline classes

Just what the heading says.

Allow a primary constructor to be "re-declared" / "re-defined" with a body

This would look like:

extension type DegreesKelvin(int degrees) {
 DegreesKelvin(int degrees) {
    if (degrees < 0) throw ArgumentError('must be positive');
  }
}

You could say the primary constructor is declared on the first line and defined in the body of the extension type. You could put in requirements that the parameter static type and name are the same as in the declaration. You could put in a lint that reports if such a constructor doesn't have a body or initializers (can just be the declaration on the first line).

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-types-laterIssues about extension types for later consideration

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions