An external class is simply put, a class definition for an already existing JavaScript object, typically to typecast the data a function in a library or the browser returns, making it easier to work with the data from an object pascal point of view. In the RTL DOM package you will find quite a lot of these external class definitions, which the IDE uses to typecast and work more elegantly with the document object model (or the NodeJS modules).


Example of external class definitions:


JCustomEventInit = class external 'CustomEventInit' (JEventInit)

 detail : Variant

end;


JEventTarget = class external 'EventTarget'

 procedure addEventListener(aType : String; callback : JEventListener; capture  : Boolean = false);

 procedure removeEventListener(aType : String; callback : JEventListener; capture  : Boolean = false);

 function  dispatchEvent(event : JEvent) : Boolean;

end;


As you probably suspect an external class does not have any implementation in your pascal code. It is purely the description of an object or method that pre-exists, either in the browser itself, or via a <script> tag that you have added to the HTML document (note: you can also load JS libraries at runtime).

Mapping to new external objects


Lets say you want to work with a JavaScript library that exposes a function called 'getDataSort'.

The function returns a JavaScript object containing an array of string and a few functions, like this:


{

 data: [];

 function first() { .. }

 function last() { .. }

 function setData(newDataArray);

}


To make this structure known to the compiler we describe it as a class and mark it with the 'external' keyword:


JSortObj = class external

 data: array of string;

 function first: string;

 function last: string;

 procedure setData(newDataArray: array of string);

end;


Next, we define the function and mark that as external as well (notice that we uppercase the leading character. This is fine since the compiler will use the external name, so we dont have to care about case as long as we get the external identifier right):


function GetDataSort: JSortObj external 'getDataSort';


With both the object and method known to the compiler we can safely access them, just like they were implemented in object pascal:


var obj := GetDataSort();

obj.SetData( ["first", "second", "third"] );

WriteLn( obj.first );


When you dont know the type


If you want to wrap a library or structure where the datatype is unknown, or where there is an array that contains different datatypes, the best way to solve this is to define it as variant.

You can then use the JsTypeOf() function is qtx.sysutils to determine the type at runtime (if you are enumerating from the array).

Variant is wonderful when dealing with native JavaScript because it acts as the glue between worlds. It is a way of saying "I dont know what the value is, but I can still work with it".

You can use the helper functions in the TVariant class to typecast any variant to an intrinsic type. The TVariant.AsObject() can cast from variant to TObject, should you need it.