2 Comments

Let’s assume we have the class [GuidGenerator] in a TypeScript module [research.my.long.namespace] and at runtime you have an instance of the GuidGenerator, how do you get the full class name including module name, in this case: “research.my.long.namespace.GuidGenerator”.

Well you walk the object graph starting with the root / global object, in the browser this will be the window object.

 

NOTE: all code is in “strict” modus.

 

TypeScript Class [GuidGenerator]:

module research.my.long.namespace {
    "use strict";

    export interface IGuidGenerator {
        generate(): string;
    }

    export class GuidGenerator implements IGuidGenerator {
        generate() {
            var randomNumberToGuid = function (c) {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : r & 0x3 | 0x8;
                return v.toString(16);
            }
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, randomNumberToGuid);
        }
    }
}

We use the resolver object to get the full class name:

var resolver = new NameResolver();
var generator = new research.my.long.namespace.GuidGenerator();
var name = resolver.getFullClassNameFromInstance(generator, window)
console.log(name);

Result

image

The resolver class

module research.metadata {
    "use strict";

    interface IProcessResult {
        fnFound: boolean;
        path: Array<string>;
    }

    export interface INameResolver {
        getFullClassNameFromInstance(instance: any, global: any): string;
    }

    export class NameResolver implements INameResolver {
        private _fn: any;
        private _global: any;
        private _processed: Array<any>;

        /**
            To handle recursiveness in the object graph, collect all handled nodes in the object graph,
            so an object is only traversed once.
        */
        isProcessed(obj: any): boolean {
            var result = false;
            for (var i, length; i < length; i += 1) {
                if (this._processed[i] === obj) {
                    return true;
                }
            }
            return result;
        }

        processProperty(obj: any, key: string, path: Array<string>): IProcessResult {
            var result: IProcessResult = {
                fnFound: false,
                path: path
            }

            if (obj.hasOwnProperty(key)) {
                try {
                    var prop = obj[key];
                    if (prop === this._fn) {
                        // Function found, stop traversing the object graph.
                        result.fnFound = true;
                        return result;
                    }
                    
                    // Continue traversing the object graph.
                    result = this.processObject(prop, path);

                    if (result.fnFound) {
                        // Function found, stop traversing the object graph.
                        return result;
                    }
                } catch (error) {
                    // Access to some properties result in exceptions.
                }
            }
            

            return result;
        }

        processObject(obj: any, path: Array<string>): IProcessResult {
            var result: IProcessResult = {
                fnFound: false,
                path: path
            }

            if (this.isProcessed(obj)) {
                return result;
            }
            this._processed.push(obj);
            
            for (var key in obj) {
                var pathCopy = path.slice();
                pathCopy.push(key);
                var processResult = this.processProperty(obj, key, pathCopy);
                if (processResult.fnFound) {
                    return processResult;
                }
            }

            return processResult;
        }

        getFullClassNameFromInstance(instance: any, global: any): string {
            this._fn = instance["constructor"];
            this._global = global;
            this._processed = [];

            var processResult = this.processObject(this._global, []);

            var fullFnName = "";
            if (processResult.fnFound) {
                fullFnName = processResult.path.join(".");
            }

            return fullFnName;
        }
    }
}

2 Replies to “How to get full class name, including module name from an instance at runtime in TypeScript

  1. I tried your code in a typescript playground and got “resolver._fn” as result … did I miss something ?

  2. To tell the thruth, your code copied and pasted in typescript playground works fine. The result “resolver._fn” is coming out when I try to apply the code to my test module:

    module app.model {
    export interface IDataModel {
    }

    export interface IBehaviorModel {
    fromDataModel(dataModel: TDataModel): TBehaviorModel ;
    }

    export class SampleDataModel implements IDataModel {}

    export class SampleBehaviorModel implements IBehaviorModel{
    public fromDataModel(dataModel: SampleDataModel){
    return new SampleBehaviorModel();
    };
    }

    export class Utils {
    public static __getClass(object) {
    return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
    }
    }
    }

    module research.metadata {
    “use strict”;

    interface IProcessResult {
    fnFound: boolean;
    path: Array;
    }

    export interface INameResolver {
    getFullClassNameFromInstance(instance: any, global: any): string;
    }

    export class NameResolver implements INameResolver {
    private _fn: any;
    private _global: any;
    private _processed: Array;

    /**
    To handle recursiveness in the object graph, collect all handled nodes in the object graph,
    so an object is only traversed once.
    */
    isProcessed(obj: any): boolean {
    var result = false;
    for (var i, length; i < length; i += 1) {
    if (this._processed[i] === obj) {
    return true;
    }
    }
    return result;
    }

    processProperty(obj: any, key: string, path: Array): IProcessResult {
    var result: IProcessResult = {
    fnFound: false,
    path: path
    }

    if (obj.hasOwnProperty(key)) {
    try {
    var prop = obj[key];
    if (prop === this._fn) {
    // Function found, stop traversing the object graph.
    result.fnFound = true;
    return result;
    }

    // Continue traversing the object graph.
    result = this.processObject(prop, path);

    if (result.fnFound) {
    // Function found, stop traversing the object graph.
    return result;
    }
    } catch (error) {
    // Access to some properties result in exceptions.
    }
    }

    return result;
    }

    processObject(obj: any, path: Array): IProcessResult {
    var result: IProcessResult = {
    fnFound: false,
    path: path
    }

    if (this.isProcessed(obj)) {
    return result;
    }
    this._processed.push(obj);

    for (var key in obj) {
    var pathCopy = path.slice();
    pathCopy.push(key);
    var processResult = this.processProperty(obj, key, pathCopy);
    if (processResult.fnFound) {
    return processResult;
    }
    }

    return processResult;
    }

    getFullClassNameFromInstance(instance: any, global: any): string {
    this._fn = instance[“constructor”];
    this._global = global;
    this._processed = [];

    var processResult = this.processObject(this._global, []);

    var fullFnName = “”;
    if (processResult.fnFound) {
    fullFnName = processResult.path.join(“.”);
    }

    return fullFnName;
    }
    }
    }

    var myModel = new app.model.SampleBehaviorModel();
    var resolver = new research.metadata.NameResolver();
    alert( resolver.getFullClassNameFromInstance(myModel, window) );

    the alert shows “resolver._fn”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Posts