The JavaScript type system seems off

I’m posting this here because I suspect at least some of it might be beyond your control and just “how Qt does things” (or even specific to Qt 5, as I’m unable to use the Qt 6 build), but…

There are a bunch of little things that are just not how one expects JavaScript to work. For example, the instanceof operator does not work. Well… that’s an exaggeration. Rather, it does not work on native Tiled types. For example:

> ({}) instanceof Object
$0 = true
> tiled.activeAsset instanceof TileMap
TypeError: Type error

Both of those should return true (assuming the active asset is, in fact, a map rather than a tileset), but as you can see, the second one raises an error instead.

Okay, fine. So instanceof doesn’t work. At least there’s a workaround!

> tiled.activeAsset.constructor == TileMap
$1 = false

Nope, that doesn’t work either. That said…

> (new TileMap()).constructor == TileMap
$2 = true

At least it works for one you constructed yourself. Maybe the issue is just that the opened asset is a subtype of TileMap. And yet…

> tiled.activeAsset
$3 = Tiled::EditableMap(0x600000537c00)
> new TileMap()
$4 = Tiled::EditableMap(0x60000050e3f0)

That doesn’t look like they’re different types to me. I don’t know if they are, or if the ones constructed by the engine are just broken, but I suspect the latter may, in fact, be the case.

If it’s indeed the case, you might think, why not just implement instanceof manually? Well, there’s a problem with that too…

> TileMap.prototype

Yup. There’s no prototype. As far as someone working in the API can tell, the TileMap class is just a plain object with a bunch of properties, some of which happen to be functions. But it does have a constructor.

I realize that for this specific case there’s a workaround:

> tiled.activeAsset.isTileMap
$6 = true

But I haven’t seen a generic workaround that works for other types, like Tile.

Speaking of other types, a lot of them are just generic objects with a bunch of properties. For example, consider ObjectRef. This exists in the documentation, but in practice it does not exist.

> ObjectRef
ReferenceError: ObjectRef is not defined

As far as I can tell, these two are completely equivalent from the viewpoint of the API:

> tiled.objectRef(22)
$7 = Tiled::ObjectRef(22)
> ({id:22})
$8 = [object object]

You can see there that they are not, in fact, identical to the runtime, but I don’t see a way for the JavaScript API to get at that info, other than by calling toString(0.

Combining the above with the fact that instanceof doesn’t work, this means there’s no simple way to determine whether you got a MapObject or an ObjectRef out from an object property. I think your only options are:

> tiled.objectRef(22).toString().includes('Ref')
$9 = true
> new MapObject().toString().includes('Ref')
$10 = false
> 'asset' in tiled.objectRef(22)
$11 = false
> 'asset' in new MapObject()
$12 = true

Has anyone else noticed little things like this? Is there anything that can be done about them? If it’s not “just how Qt does things”, I’m happy to open one or more issues about this. Even if it is “just how Qt does things”, perhaps I could open some issues regarding adding workarounds to the API, but it might be better to first discuss what those workarounds could look like.

From asking similar questions previously, the answer is mostly “just how Qt does things”. There are a lot of little things like this, mostly stemming from the fact that JS and C++ are different worlds with different ideas about where things exist and who owns them.

The type stuff in particular I care little about because even JS-native types can get weird, but other symptoms like let property = tileset.someProperty; property.subproperty = newValue not working as you might expect (the tileset’s someProperty’s subproperty isn’t updated until you assign to tileset.someProperty) can cause mischief even when you’re aware of this behaviour.

I’m wary of implementing workarounds because they can make extending the API more of a hassle and can lead to inconsistencies when the workarounds are forgotten, which could make the medicine worse than the disease xP Even seemingly one-and-done workarounds can cause problems down the line as new types and behaviours are added.
Qt isn’t a total stranger to making improvements on their end, so I think it would be better to report issues to them, though that does need someone who can explain relevant specifics of Tiled’s integration as part of the report. They have already resolved some of the weirdness reported via Tiled :]

If it’s mostly, does that mean that some things here are under Tiled’s control?

Well, there do exist a few workarounds already – like the isTileMap and isTileLayer properties (and 4 other similar properties). A very simple workaround for the map objects case would be to add an isResolved property on MapObject that’s always true, and also add it to ObjectRef but there it’s always false.