Advanced Types

Up to now, we have seen the following types :

  • class instance
  • enum instance
  • dynamic

There are several additional important types.

Structure

A structure type is the type of an anonymously declared object. It is also the type of a Class identifier (corresponding to all the static fields) or an Enum identifier (listing all the constructors). Here's an example that shows it:

    enum State {
        on;
        off;
        disable;
    }

    class C {
        static var x : Int;
        static var y : String;

        function f() {
            // print { id : Int, city : String }
            trace({ id : 125, city : "Kyoto" });
        }

        function g() {
            // print { on : State, off : State, disable : State }
            trace(State);
        }

        function h() {
            // print { x : Int, y : String }
            trace(C);
        }
    }

Typedef

You can define type definitions which are a kind of type shortcut that can be used to give a name to a structure type or a long type that you don't want to repeat everywhere in your program :

typedef User = {
    var age : Int;
    var name : String;
    @:optional var location:String;//since haxe 2.09
}
// ....
var u : User = { age : 26, name : "Tom" };

The same code can also be written as:
typedef User = {
    age : Int,
    name : String,
    ?location:String//since haxe 2.09
}
// ....
var u : User = { age : 26, name : "Tom" };

// PointCube is a 3-dimensional array of points
typedef PointCube = Array<Array<Array<Point>>>

Typedefs are not classes, they are only used for typing.

As aliases

You can "alias" types using type definitions in the following manner:

class MyClassWithVeryLongName { }
typedef MyClass = MyClassWithVeryLongName;

You can assign any kind of type like this, not just classes - enums, interfaces and other type definitions can be aliased as well in the similiar manner. The resulting type identifiers can be used wherever you would otherwise be using the class, enum, or whatever.

Private members

Typedefs can contain private members in their definitions. This allows access to otherwise unavailable members.

class Foo {
    public function new() {}
    public function fullAccess() return true
    function restrictedAccess() return true
}

typedef Bar = {
    function fullAccess():Bool;
    private function restrictedAccess():Bool;
}

class Main {
    public static function main()
    {
        var foo = new Foo();
        foo.fullAccess();
        //Compiler Error: Cannot access to private field restrictedAccess
        foo.restrictedAccess();
        
        var bar:Bar = foo;
        bar.fullAccess();
        //No Error, this is allowed
        bar.restrictedAccess();
    }
}

Private types

A type - class, typedef, interface or enum - can be defined as 'private', which will confine its visibility only to the module it is defined in. Public types belong to packages they are defined in, and they can only be defined once. With private type definitions however you can have a "local" type only visible within the module it is defined in. Another private type with the same name and in the same package can be defined in another module.

private typedef MyType =
{

}

Accessing Typedefs From Other Packages


It is often useful to use a typedef from another package for a variable declaration. This can be accomplished in the same ways that internal helper classes are referenced. For a given typedef "MyType":
// in Foo.hx
typedef MyType =
{ ... }

The first way is to simply import the Class containing the Typedef you wish to use:

// in Bar.hx
import Foo;
class Bar{
    var mt:MyType;
}

The second way is to use a special extended package definition, which allows you to avoid using import:

// in Bar.hx
class Bar{
    var mt:Foo.MyType;
}

Functions

When you want to define function types to use them as variables, you can define them by listing the arguments followed by the return type and separated with arrows. For example Int -> Void is the type of a function taking an Int as argument and returning Void. Color -> Color ->Int takes two Color arguments and returns an Int.

    class C {
        function f(x : String) : Int {
            // ...
        }

        function g() {
            type(f); // print String -> Int
            var ftype : String -> String = f;
            // ERROR : should be String -> Int
        }
    }

Unknown

When a type is not declared, it is used with the type Unknown. The first time it is used with another type, it will change to it. This was explained in more detail in Type Inference. The id printed with the Unknown type is used to differentiate several unknowns when printing a complex type.

    function f() {
        var x;
        type(x); // print Unknown<0>
        x = 0;
        type(x); // print Int
    }

The diversity of types expressible with Haxe enable more powerful models of programming by providing high-level abstractions that don't need complex classes relationships to be used.

Extensions

Extensions can be used to extend either a typedef representing an anonymous type or to extend a class on-the-fly.

Here's an example of anonymous typedef extension :

   typedef Point = {
       var x : Int;
       var y : Int;
   }

   // define 'p' as a Point with an additional field z
   var p : {> Point, z : Int }
   p = { x : 0, y : 0, z : 0 }; // works
   p = { x : 0, y : 0 }; // fails

For classes, since they don't define types, you need to use a cast when assigning, it's unsafe so be careful :

   var p : {> flash.MovieClip, tf : flash.TextField };
   p = flash.Lib._root; // fails
   p = cast flash.Lib._root; // works, but no typecheck !

You can also use extensions to create cascading typedefs :

    typedef Point = {
        var x : Int;
        var y : Int;
    }
    typedef Point3D = {> Point,
        var z : Int;
    }

In that case, every Point3D will of course be a Point as well.

Cascading typedefs can be created from normal class types as well, but the compiler will not be able to match this typedef against an anonymous object, or any other instanced class. This is because Haxe is not fully structurally subtyped. Structural subtyping can only occur between anonymous typedefs or between an instance and an anonymous typedef.

Historical feature? See https://github.com/HaxeFoundation/haxe/issues/2456

«« Dynamic | Iterators »»

version #19846, modified 2013-12-11 16:56:39 by MarcWeber