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

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 Comments

  1. Gabriele Rossetti

    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 *