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:
Iterable<String>
andlength
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(); } }
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.