Object construction - Haxe style

In my day-to-day, I’m working with JS (surprise!)
I’m used to manipulate dynamic objects very easily:

const a = { a: 1, b: 2 };
a.c = 3;

Haxe, however, infers the type by the first assignment and won’t let you set the second assignment, so we have to declare the variable as Dynamic.
How can I manipulate objects in haxe more easily?
Most of the time I have this problem is when I’m doing an HTTP post request:

var body = { a:1, b: 2 };
var response = makePostRequest(body);
body.id = resonse.id;
doSomethingWithResponse(body);

I’m not looking for an exact “match” with the JS style (which involves, of course, Dynamic) but a glance of how a Haxe programmer should approach to this “problem”.
Thanks

you can define a type with a typedef, to allow all expected future fields,

typedef Foo = { a:Int, b:Int, ?c:Int };

class Main
{
    static function main()
    {
        var foo:Foo = { a:1, b: 2 };
        foo.c = 3;
    }

?c:Int means c is optional, and null by default, you can do the same without a typedef, too

var foo:{ a:Int, b:Int, ?c:Int } = { a:1, b: 2 };
foo.c = 3;

but with typedefs you can always add fields to them and every place that uses the type will accept that new field.

2 Likes

The answer given by @Geokureli is spot on. If you wish to skip explicit type declarations, the following will do the trick:

var body = { a:1, b: 2, id: null };
var response = makePostRequest(body);
body.id = resonse.id;
doSomethingWithResponse(body);

Thanks for both of you!

@back2dos The problem with the approach you suggest, is that I’m using tink_web and do something like this:

var trainee = {
  start: training.start,
  finish: training.finish,
};

Client.remote.trainings().byId(training.id).trainees().add(soldierId, trainee)
.handle((o) -> {
  switch (o) {
    case Success(response): 
      // take the trainee id from the response and create a client side trainee object.
    case Failure(err): trace(err);
  }
});

But I can’t send id null in the add function because it’s not the type tink_web expect for.

This may be the culprit:

var trainee = {
  start: training.start,
  finish: training.finish,
};

Because you never specified a type, Haxe inferred the type based on the variables that currently exist. And by passing this to add(), you may have told it that you’re expecting a response of the same form. (I wasn’t actually able to find the add() function to confirm/deny this. I just know it’s a thing that can happen in Haxe.) If that’s the case, then that explains why you aren’t able to add new fields. Haxe believes it has an exhaustive list of the object’s fields and won’t (easily) let you add new ones.

The solution is to follow Geokureli’s example a little more closely:

//Put this outside the class, optionally in a file of its own.
typedef Trainee = {
    start:Int,
    finish:Int,
    ?id:Int
};

//...

var trainee:Trainee = {
    start: training.start,
    finish: training.finish
};

The instantiated object has the exact same fields as before. You’re sending the same amount of data over the web. But when you get a response, Haxe will expect it to be a Trainee, meaning that it’ll let you add an id field.