Get intance's class in TypeScript
Asked Answered
P

1

11

How to get the class of an instance in TypeScript? This question is similar to Get an object's class name at runtime in TypeScript but I don't want to get the class name. I want to get the class itself.

What I need this for? TypeScript does not have class methods, only static methods. But they are not virtual. In order to fully translate my mental model into Js code, I need to have virtual class methods.

My idea is to do something like this:

function classOf<T>(o: T) : any {
    return o.class; // How to write this???
}

class Foo {
    static tryMe() : string {
        return "foo";
    }

}

class Bar extends Foo {
    static tryMe() : string {
        return "bar";
    }

}

function testPolymorphClassMethod(instance : Foo) {
    console.log(classOf(instance).tryMe());
}

testPolymorphClassMethod(new Foo()); // Should print "foo"
testPolymorphClassMethod(new Bar()); // Should print "bar"

Whenever a concrete class is available, I could call its static method directly. If only an instance is available, I would like to call the static method of its class, e.g. making it virtual by hand.

The problem is that I have no idea how to write the classOf function. It may not be possible at all.

I was also trying this:

function classOf<T>(o: T) : typeof T {
    return typeof o;
}

But it gives a comiple error:

"T only refers to a type, but it is used as value here".

I know that functions are first class objects in JavaScript (and also TypeScript). But TypeScript classes may not be, and then maybe it will be impossible to do what I want.

Is there a workaround for this that does not require typing lots of code for each and every descendant class?

The closest thing I could get is:

class Virtualizer {
    virtual(methodName: string) : any {
        return eval(this.constructor.name+"."+methodName);
    }
}

class Foo extends Virtualizer {
    static getMyName() : string {
        return "foo";
    }

    public test() : string {
        return this.virtual("getMyName")();
    }

}

class Bar extends Foo {
    static getMyName() : string {
        return "bar";
    }    
}

let foo = new Foo();
let bar = new Bar();

console.log(foo.test()); // Prints "foo"
console.log(bar.test()); // Prints "bar"

But this looses all type information, it needs a common base class (or code repetition), and I can never call the actual method that I want to call, just a "virtualizer". It is so clumsy.

I could extract the class related information into an encapsulated singleton object, but that will require me to change visibility of many methods from protected to public. (And also generate lots of cross references between the main and the encapsulated objects, very clumsy too.)

Pander answered 25/1, 2018 at 7:1 Comment(0)
G
13

You can return constructor property of your o

function classOf<T>(o: T): any {
    return o.constructor;
}

See Demo

Gelsemium answered 25/1, 2018 at 7:19 Comment(4)
That is amazing! How could I have known that I can access static methods through the constructor object? The constructor object is not a class, and static methods should be called on classes. This is very counter intuitive! Is this documented somewhere?Pander
Constructor just returns that function which was used to create that object. Class is a function in TS/JS, which can instantiate an object. Class is just object-oriented way of creating objects.Gelsemium
Oh okay thanks. Actually I have known that in plain Js a class is just a function, but I forgot it over time. :-)Pander
I see an error in the demo. "Property 'constructor' does not exist on type 'T'."Deadpan

© 2022 - 2024 — McMap. All rights reserved.