3.2.1 Constraints

Type parameters can be constrained to multiple types:

typedef Measurable = {
  public var length(default, null):Int;
}

class Main {
  static public function main() {
    trace(test([]));
    trace(test(["bar", "foo"]));
    // String should be Iterable<String>
    // test("foo");
  }

  #if (haxe_ver >= 4)
  static function test<T:Iterable<String> & Measurable>(a:T) {
  #else
  static function test<T:(Iterable<String>, Measurable)>(a:T) {
  #end
    if (a.length == 0)
      return "empty";
    return a.iterator().next();
  }
}

The test method contains a type parameter T that is constrained to the types Iterable<String> and Measurable. The latter is defined using a typedef for convenience and requires compatible types to have a read-only property named length of type Int. The constraints then indicate that a type is compatible if:

  • it is compatible with Iterable<String> and
  • has a length property of type Int.

In the above example, we can see that invoking test with an empty array on line 7 and an Array<String> on line 8 works fine. This is because Array has both a length property and an iterator method. However, passing a String as argument on line 9 fails the constraint check because String is not compatible with Iterable<T>.

When constraining to a single type, the parentheses can be omitted:

class Main {
  static public function main() {
    trace(test([]));
    trace(test(["bar", "foo"]));
  }

  static function test<T:Iterable<String>>(a:T) {
    return a.iterator().next();
  }
}
since Haxe 4.0.0

One of the breaking changes between versions 3 and 4 is the multiple type constraint syntax. As the first example above shows, in Haxe 4 the constraints are separated by an & symbol instead of a comma. This is similar to the new structure extension syntax.