Question

export class Base {
    static getSomething():typeof this //<-- change this?
    {
        var type = typeof this;
        return new this['type']();
    }
}
export class Foo extends Base {
    publicVar:string = 'getThis!';
}

var foo:Foo = Foo.getSomething();
var success:string = foo.publicVar;

the above returns an error because the compiler says Foo.getSomething will return a Base, not a Foo. I wonder if there is another way that lets me call a static function of Foo that the compiler knows will return a Foo.

Ofcourse I can implement it explicitly in Foo, and thus in every class that extends Base, or I could simply typecast it:

var foo:Foo = <Foo> Foo.getSomething();

But I was wondering if there is a way without having to do either of those things, since I'll be using this method a lot

Was it helpful?

Solution

Getting Rid Of The Type Assertion

If you follow the Liskov substitution principle, you don't need the type assertion.

Code Example 1 - Substitutable Objects...

module Test {
    export class Base {
       publicVar: string = 'base';

       static getSomething() : Base  {
            //... read on...
       }
    }

    export class Foo extends Base {
        publicVar:string = 'getThis!';
    }
}

// No <Foo> type assertion needed
var foo: Test.Foo = Test.Foo.getSomething();

alert(foo.publicVar);

Alternatively, you could create an interface that tells you the object returned will have a publicVar property and return that...

Code Example 2 - Interface

module Test {
    export interface IPublicVarable {
        publicVar: string;
    }

    export class Base {
       static getSomething() : IPublicVarable  {
            //... read on...
       }
    }

    export class Foo extends Base {
        publicVar:string = 'getThis!';
    }
}

// No <Foo> type assertion needed
var foo: Test.IPublicVarable = Test.Foo.getSomething();

alert(foo.publicVar);

Getting the Actual Type

This doesn't solve one other issue you have though - var type = typeof this; isn't going to give you what you expect at runtime. It is going to give you Function not Foo.

To get a type name, you really need to work with an instance (if you use Test.Foo the type name is Function once again - which does you no good), so here is an imperfect example using two different subclasses that both satisfy the interface, based on my Obtaining a Class Name at Runtime example:

module Test {
    export class Describer {
        static getName(inputClass) { 
            var funcNameRegex = /function (.{1,})\(/;
            var results = (funcNameRegex).exec((<any> inputClass).constructor.toString());
            return (results && results.length > 1) ? results[1] : "";
        }

        static getInstanceOf(inputClass) : Test.IPublicVarable {
            var name = Describer.getName(inputClass);
            return new Test[name]();
        }
    }

    export interface IPublicVarable {
        publicVar: string;
    }

    export class Base {

    }

    export class Foo extends Base {
        publicVar:string = 'foo class';
    }

    export class Bar extends Base {
        publicVar:string = 'bar class';
    }
}

var a: Test.Base = new Test.Foo();
var x: Test.IPublicVarable = Test.Describer.getInstanceOf(a);
alert(x.publicVar);

var b: Test.Base = new Test.Bar();
var y: Test.IPublicVarable = Test.Describer.getInstanceOf(b);
alert(y.publicVar);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top