Haxe 3 Migration

Compatibility tools

The hx2compat library provides some haxe 2 type definitions that were removed from haxe 3. It can be used for easier transition.

convert-haxe3 is a Python script that automates some of the refactoring and syntax changes below.

Arrays of mixed types

  • Symptom: Compiler error Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>
  • Explanation: Haxe 2 allowed Array declarations such as [1, "foo"] and inferred the type to Array<Dynamic>. Haxe 3 does allow these only if the type is explicitly set to Array<Dynamic>.
  • Fix: Force the Array declaration to be typed against an explicit Array<Dynamic>. This includes the following cases:
    • assign it to a variable which is typed as Array<Dynamic>: var a:Array<Dynamic> = [1, "foo"];
    • assign it to a field which is typed as Array<Dynamic>
    • use it as argument to a function expecting an Array<Dynamic>
    • return it from a function that is declared to return an Array<Dynamic>
  • Code example:
    // haxe 2
    var x = [1, "foo"];
    // haxe 3
    var x:Array<Dynamic> = [1, "foo"];


  • Symptom: Compiler error callback syntax has changed to func.bind(args)
  • Explanation: Haxe 2 had a callback keyword which could be used for partial application. In haxe 3 the special field bind can be invoked on functions.
  • Fix: Replace callback(func,args) with func.bind(args)
  • Code example:
    function add(a, b) return a + b;
    // haxe 2
    callback(add, 1);
    // haxe 3

Generalization of sys platform APIs

  • Symptom: Class not found errors for the following packages:
    • cpp, cpp.io, cpp.net
    • neko, neko.db, neko.io, neko.net, neko.zip
    • php, php.db, php.io, php.net
  • Explanation: Haxe 3 generalized several APIs of sys-platforms in the sys package. The platform-specific versions from haxe 2 were removed.
  • Fix: Use the platform-independent classes in sys, sys.db sys.io and sys.net instead.

Hash and IntHash removal

  • Symptom: Compiler error Hash has been removed, use Map instead or IntHash has been removed, use Map instead
  • Explanation: Haxe 2 provided Hash and IntHash as toplevel classes. In haxe 3 their implementations was moved to the haxe.ds package and a general-purpose Map type remains in the toplevel.
  • Fix:
    • Replace Hash with Map, possibly adding an explicit String key if required
    • Replace IntHash with Map, possibly adding an explicit Int key if required
  • Code example:
    // haxe 2
    var hash = new Hash();
    var intHash = new IntHash();
    var typedHash : Hash<MyClass>;
    // haxe 3
    var hash = new Map();
    var intHash = new Map();
    var typedHash : Map<String,MyClass>;
    hash.set("foo", 1);
    intHash.set(1, 2);

haxe.rtti interfaces

  • Symptom: Compiler error Use @:generic instead of implementing haxe.Generic or Use @:rtti instead of implementing haxe.rtti
  • Explanation: Haxe 2 used the special interfaces haxe.rtti.Generic and haxe.rtti.Infos to enable particular behavior. In haxe 3 they have been replaced by @:generic and @:rtti metadata respectively.
  • Fix:
    • Remove implements haxe.rtti.Generic and add @:generic metadata to the class
    • Remove implements haxe.rtti.Infos and add @:rtti metadata to the class
  • Code example:
    // haxe 2
    class GenericClass extends haxe.rtti.Generic { }
    class RttiClass extends haxe.rtti.Infos { }
    // haxe 3
    @:generic class GenericClass { }
    @:rtti class RttiClass { }

Initialization of inline variables

  • Symptom: Compiler error Variable initialization must be a constant value
  • Explanation: Haxe 2 allowed arbitrary expressions as initialization to inline variables. This could cause unexpected behavior because expressions with potential side-effects were injected to the place of their access. Haxe 3 restricts the allowed expressions to constant ones, i.e. those that have no side-effects.
  • Fix: Use an inline function returning the value instead.
  • Code example:
    // haxe 2
    static inline var x = [];
    // haxe 3
    static inline function x() {
        return [];


  • Symptom: Compiler error haxe.Int32 has been removed, use normal Int instead or errors related to readUInt30, readInt31, writeUInt30, writeInt31
  • Explanation: Haxe 2 provided the haxe.Int32 Api to enable 32-bit integer operations across all targets. This is no longer necessary for haxe 3, so the Api has been removed.
  • Fix:
    • replace all occurences of Int32 with Int
    • replace readUInt30 and readInt31 with readInt32
    • replace writeUInt30 and writeInt31 with writeInt32

Interface syntax changes

  • Symptom: Compiler error Interfaces cannot implements another interface (use extends instead) or Unexpected ,
  • Explanation: In haxe 2 interfaces would use implements to extend other interfaces, and multiple extends and implements declaration were separated by a comma. In haxe 3 interfaces use extends instead and the separating comma is no longer allowed.
  • Fix:
    • replace implements with extends on interface declarations
    • remove the comma separating multiple implements and extends declarations
  • Code example:
    // haxe 2
    interface InterfaceSyntax { }
    interface InterfaceSyntax2 implements InterfaceSyntax { }
    class InterfaceSyntaxClass extends haxe.Template, implements InterfaceSyntax2 { }
    // haxe 3
    interface InterfaceSyntax { }
    interface InterfaceSyntax2 extends InterfaceSyntax { }
    class InterfaceSyntaxClass extends haxe.Template implements InterfaceSyntax2 { }

Javascript API changes

  • Symptom: Various Class not found or ... has no field errors
  • Explanation: The HTML5 API has been greatly extended in haxe 3, making js.Dom in particular obsolete. Everything is now available within the js.html package.
  • Fix:
    • replace js.Lib.document with js.Browser.document
    • replace js.Lib.window with js.Browser.window
    • replace js.XMLHttpRequest with js.html.XMLHttpRequest or js.Browser.createXMLHttpRequest
    • replace js.Storage with js.Browser.getLocalStorage/getSessionStorage
    • replace unknown types previously in js.Dom with their equivalent in js.html

Javascript modern mode

  • Symptom: Errors when trying to access haxe generated types or functions from other code, such as inline Javascript code in HTML.
  • Explanation: Haxe 2 provided the -js-modern command line switch, which would wrap the entire generated code in a function like so:
    (function() {
    // JS code ...
    This behavior is now the default in haxe 3, preventing direct access of types and variables in some cases.
  • Fix: The behavior can be completely disabled with -D js-classic. If only a few types or functions have to be exposed, the @:expose metadata can be used on them.
  • Code example:
    // allows access to MyClass from my HTML code
    @:expose class MyClass {

Javascript override changes

  • Sympom: Unexpected behavior when accessing one of the following methods on an object typed as Dynamic
    • Array.insert, Array.remove, Array.copy, Array.iterator
    • String.substr, String.charCodeAt
    • Date.toString, Date.now, Date.fromTime, Date.fromString
  • Explanation: Haxe 2 used to modify the prototypes of some types directly, which could lead to several issues. In haxe 3 these methods are now static functions in js.HxOverrides and the compiler rewrites field access accordingly. However, this is not possible if the type the field access is made on is of Dynamic type.
  • Fix: Make sure the field in question is correctly typed, e.g. by assigning it to a variable.
  • Code example:
     var a:Array<Int> = cast some.Class.someFieldTypedAsDynamic;
     a.remove(1); // works as expected now 

Macro function changes

  • Symptom: Compiler error Warning : @:macro should now be 'macro' accessor'
  • Explanation: In Haxe 2, macro functions were defined using "@:macro" metadata. In Haxe 3, they now have an accessor keyword, similar to "static" or "public".
  • Fix:
    • replace @:macro with 'macro'
  • Code example:
    // haxe 2
    @:macro public static function myMacro(str:String) { ... }
    // haxe 3
    macro public static function myMacro(str:String) { ... }

Macro reification changes

  • Symptom: Various compiler errors when using macro reification. (A compiler error related to macros is "Defined in this class". If you have errors in other classes, this error may appear as a side effect of general compilation errors. Fix your other classes before troubleshooting this error message).
  • Explanation: The reification syntax of haxe 2 has been changed for haxe 3. While now being more flexible and readable, it likely breaks haxe 2 reification code.
  • Fix:
    • replace $(value) with $v{value}
  • Code example:
    // haxe 2
    var v = macro $(1);
    // haxe 3
    var v = macro $v{1};

Order of import and using

  • Symptom: The compiler infers identifiers to the wrong type.
  • Explanation: Haxe 2 resolved unknown identifiers by checking the list of imported modules from top to bottom. Haxe 3 checks this list from bottom to top.
  • Fix: Reverse the declarations of import and using in a file that causes problems.

Property accessors

  • Symptom: Compiler error Custom property accessor is no longer supported, please use get/set
  • Explanation: In haxe 2 it was possible to use custom names for the getter and setter of a property. Haxe 3 enforces the naming scheme of get_propertyName and set_propertyName for the getter and setter respectively.
  • Fix: Fixing this involves two steps per property:
    • use get and set in the property declaration, e.g. var prop(get,set):Int;
    • rename the accessor function to get_propertyName and set_propertyName for the getter and setter respectively
  • Code example:
    // haxe 2
    var property(getProperty, setProperty):Int;
    function getProperty() return 1
    function setProperty(i) return 1
    // haxe 3
    var property(get, set):Int;
    function get_property() return 1;
    function set_property(i) return 2;

Property field creation

  • Symptom: Compiler error This field cannot be accessed because it is not a real variable
  • Explanation: In haxe 2 properties always had a real field representation, even if they did not need one. In particular, this applied to pure getter/setter properties. Haxe 3 treats properties as real variables only if they have a default or null access.
  • Fix: Add @:isVar to the property in question.
  • Code example:
    // haxe 2
    var property(get, set):Int;
    function get_property() { return property; }
    function set_property(i) {return property = i; }
    // haxe 3
    @:isVar var property(get, set):Int;
    function get_property() { return property; }
    function set_property(i) {return property = i; }

String Interpolation

  • Symptom: Compiler error Std.format has been removed, use single quote 'string ${escape}' syntax instead
  • Explanation: Haxe 2 was using a haxe macro accessible as Std.format() for String interpolation. In haxe 3 single quotes syntax can be used instead.
  • Fix: Remove the call to Std.format and enclose the String by ' instead of ".
  • Code example:
    var x = 12;
    // haxe 2
    var s = Std.format("x equals $x");
    // haxe 3
    var s = '$x * $x equals ${x * x}';
    trace(s); // 12 * 12 equals 144

If you're having some code using $ and single quotes strings, you can compile with -D format-warning, this will print all the locations were such patterns are found. You can then replace the $ with $$ or use double quotes strings to prevent unwanted formatting.

Switch changes

  • Symptom: Various compiler errors or warnings in a switch expression
  • Explanation: By default Haxe 3 uses pattern matching. While this enables a concise and expressive way for many cases, it may introduce a few incompatibilities to the switch expression used in Haxe 2.
  • Fix: Adding -D no-pattern-matching to the command line will disable pattern matching and retain the haxe 2 behavior. Otherwise it might be worthwhile to adjust the case expressions to conform with the new switch specification. This includes:
    • null patterns are not allowed and should be replaced by a guard-expression: case CTor("foo",x) if (x == null):
    • Unused variables will generate a warning because they might have a negative impact on the code output. They can be replaced with _ or an identifier starting with _ in case they are still refered to in documentation.
    • The | operator is used as or-operator and cannot be used to do in-case integer calculation anymore. These should be moved to e.g. an inline variable.
    • Some rare case-patterns may no longer be supported, in which case they can be replaced with a if .. else if ... else chain.
    • A compiler error Capture variables must be lower-case may indicate that a type name or enum constructor could not be found. Make sure that the intended type can be resolved.

Various Api changes

  • General:
    • haxe.BaseCode -> haxe.crypto.BaseCode
    • haxe.Md5 -> haxe.crypto.Md5
    • haxe.SHA1 -> haxe.crypto.Sha1
    • EReg.customReplace -> EReg.map
    • StringTools.isEOF -> StringTools.isEof
    • IntIter -> IntIterator
    • haxe.Stack -> haxe.CallStack
    • haxe.FastList -> haxe.ds.GenericStack
    • haxe.FastList.FastCell -> haxe.ds.GenericStack.GenericCell
  • neko:
    • neko.zip.Reader -> haxe.zip.Reader
    • neko.zip.Writer -> haxe.zip.Writer

New Keywords

In order to support abstract types, an abstract keyword has been added to the language. If you use this as an identifier or a package name, the compiler will reject it in haxe3.

version #19672, modified 2013-08-22 05:14:58 by jason