Day 6: Research to the Future!

This is yet another day on which the amount of code checked into the repository is lacking due to time spent working on research and the best way to tackle things. Even though I’m working in a separate branch in the repository I like to keep an outstanding large change like this that I’m not sure of as an outstanding change so that I can use the facilities of my IDE to easier track where all potential changes are.

The situation as it currently exists is this:

In the original JavaScript version of the code, Entity instances have a field named properties (a plain old Object) which contains key->value pairs that control how they operate at run time. For example, all entities have an “id” property that is a unique value that identifies them, so that other entities can look them up.

In operation, this system allows for every subclass of Entity to bring its own unique properties to the mix, but it also allows for the constructor of each subclass of Entity to also apply default values for some required properties that are missing.

By way of example, one of my projects currently has code such as:

nurdz.sneak.ChronoEntity = function (name, stage, x, y, properties, zOrder, debugColor)
{
    "use strict";

    // Modify the list of default properties to make sure that all entities get a visibility property that
    // defaults to true
    this.defaultProperties = nurdz.copyProperties (this.defaultProperties || {}, {
        visible: true,
        facing:  "right"
    });

    // Call the super class constructor. 
    nurdz.game.Entity.call (this, name, stage, x, y, 32, 32, properties || {}, zOrder, debugColor);
}

So any instance of ChronoEntity that is created will always have a property of visible set to true and facing set to “right”, unless the properties object provided in the constructor call already specifies them.

One issue I have with this system is that there is nothing to validate that the constructor is called with a sensible properties object. For example, if you misspell “facing”, the property is still there, but not as a value that you might expect.

TypeScript has the notion of interfaces as type shapes. For example, a possible definition of the above properties interface in TypeScript might be:

interface ChronoEntityProperties
{
    id? : string;
    facing? : string;
    visible? : boolean;
}

This specifies that in order to confirm to the ChronooEntityProperties interface, an object has to be known to contain at most these three values, and any other values are compile time errors. The question mark marks the interface members as being optional; they can be there or not be there, but if they are there the compiler ensures that they have the right type.

Now, in practice there is such an interface, EntityProperties which describes just the id property. When you subclass entity, (say for example ChronoEntity), you would also subclass the interface. So it would look something like this:

interface EntityProperties
{
    id? : string;
}

interface ChronoEntityProperties extends EntityProperties
{
    visible? : boolean;
    facing? : string;
}

The problem now becomes that in the base Entity class, the method for applying defaults wants to take a value of type EntityProperties regardless of subclass. The weak typing of JavaScript makes this all work out OK, but TypeScript gets a little snippy about it.

I have a potential solution to the problem, but due to the way that TypeScript transpiles down to JavaScript, it requires strict code formatting; essentially you need to redeclare a property to change its type, then make sure to set its value in the constructor prior to calling super() and also make sure that every property in the class is set in the constructor only and not initialized in its property declaration.

Probably I’m overlooking something very obvious here. I have a suspicion I should be using generics to define the interfaces, but more research is needed to determine exactly how TypeScript generics relate to the semantics of Java, of which I’m more familiar.

In a nutshell, I want to be able to do what I want to do with the least amount of required effort on the part of subclasses, which is important because if it’s complicated I’m going to keep forgetting how it works.