Herr Derb Herr Derb - 1 month ago 20
TypeScript Question

How to do a runtime cast

I'm on a web application that I write in TypeScript. In one part of the application, the use can write an additional JavaScript function, that will be parsed at runtime (

new function(Function as String)
) for execution. The code will give me back an object which I defined in TypeScript as
class Script
. This script object should contain specific functions, otherwise it's invalid. Meaning, I want to throw an exception, if one or more functions are not implemented by the user.

A Typescript cast won't do it, as it is a compile time cast.

I thought about giving the
Script
object a constructor that takes the parsed object (that, by key/values, should be a
Script
object already) and check the object in the constructor for missing properties.
Something like this:
(This will not work, it only should show the idea)

export class Script {
constructor(object: Script) {
this.getDefaultValue = object.getDefaultValue;
this.isAvailable = object.isAvailable;
this.isValid = object.isValid;
this.isInRange = object.isInRange;
this.isDataFormat = object.isDataFormat;

for (let propertie in this){
if (!this[propertie]){
throw new Error(propertie+ ' is missing.');
}
}
}
getDefaultValue: any;
isAvailable: (containerSetId: number) => boolean;
isValid: (value: any) => boolean;
isInRange: (value: any) => any;
isDataFormat: (value: any) => boolean;
}


But isn't there a nicer way to do this?

Answer

You can't use that because:

class A {
    member1: string;
    member2: number;
    member3: boolean;

    constructor() {
        this.member3 = true;
    }
}

Compiles into:

var A = (function () {
    function A() {
        this.member3 = true;
    }
    return A;
}());

As you can see, the member1 and member2 are not part of the compiled js version.
You'll need to keep track of the required properties for runtime, something like:

class Script {
    getDefaultValue: any;
    isAvailable: (containerSetId: number) => boolean;
    isValid: (value: any) => boolean;
    isInRange: (value: any) => any;
    isDataFormat: (value: any) => boolean;

    required = [
        "getDefaultValue",
        "isAvailable",
        "isValid",
        "isInRange",
        "isDataFormat"
    ]

    constructor(object: Script) {
        this.getDefaultValue = object.getDefaultValue;
        this.isAvailable = object.isAvailable;
        this.isValid = object.isValid;
        this.isInRange = object.isInRange;
        this.isDataFormat = object.isDataFormat;

        for (let propertie in this.required) {
            if (!this[propertie] || typeof this[propertie] !== "function") {
                throw new Error(propertie+ ' is missing.');
            }
        }
    }
}