1

I have some static stuff in all my hierarchy (in this example, the _image). I would want to be able to access the correspondant _image without having to repeat the code:

This would be great:

class Actor {
    static _image; // I need it to be static

    method show(){  // I need it to be non-static
        this.setImage(this.class._image); //doesn't work....
    }
}

class GoodActor extends Actor {
    static _image = 'good.png'
}

class BadActor extends Actor {
    static _image = 'bad.png'
}

class MediumActor extends Actor {
    static _image = 'medium.png'
}

But it doesn't work. Right now I only got to:

class Actor {
}

class GoodActor extends Actor {
    static _image = 'good.png'  // I need it to be static

    method show(){   // I need it to be non-static
        this.setImage(GoodActor._image);
    }
}

class BadActor extends Actor {
    static _image = 'bad.png'  // I need it to be static

    method show(){  // I need it to be non-static
        this.setImage(BadActor._image);
    }
}

class MediumActor extends Actor {
    static _image = 'medium.png'  // I need it to be static

    method show(){  // I need it to be non-static
        this.setImage(MediumActor._image);
    }
}

Suppose those four classes have more methods. I don't want to have to repeat the show() method in every subclass... Yet I need the show() method to be non static and the _image to be statically accessed.

I have read this issue https://github.com/Microsoft/TypeScript/issues/7673 but unfortunatelly I can't ask there as they have closed it without fixing it. None of them talked about this problem of needing to dynamically resolve the static method to be invoked.

Alf Sanzo
  • 141
  • 2
  • 9
  • 3
    Why exactly do you *need* to have it static? Using static variables for variable things normally scream "there's a problem". – fjc Oct 09 '18 at 16:19
  • Hi!! Yes, I know is kind of strange. It is a web - game. I need to have the images of the actors prior to construction of the actor in order to be able to pre-load them. Suggestions accepted :) – Alf Sanzo Oct 09 '18 at 16:31
  • Place images in some kind of static object unrelated to these and load them from there. – Roberto Zvjerković Oct 09 '18 at 16:33

2 Answers2

3

Update: Why don't you want to construct the individual objects prior to caching images? If you make construction inexpensive, there's no benefit of using static fields.

Example:

class Actor {
    image: string;

    showImage() {
        console.log(this.image);
    }
}


class GoodActor extends Actor {
    image = 'good.png';
}

class BadActor extends Actor {
    image = 'bad.png';
}

const myActorTypes: (typeof Actor)[] = [GoodActor, BadActor];

function preloadImages() {
    for (let myActorType of myActorTypes) {
        preloadImage(new myActorType().image);
    }
}

function preloadImage(image: string) {
    console.log(`Loading ${image}`);
}

preloadImages();

// "Loading good.png"
// "Loading bad.png"
fjc
  • 5,590
  • 17
  • 36
  • You hit the exact point I'm dealing with, in the new Update you made. That's exactly what I want to do: have the classes in an array and forEach to them. The problem is that, as you noted, construction *is* expensive here. The actor shows itself the moment you do new myActorType(). The constructor does this.show() and calculates bunch of stuff. And in a game you want actors in different moments... You don't want them all at once. Changing this design is quite a big job right now. I'm trying to find some workaround. Right now I've learned "don't have expensive constructions", for the future. – Alf Sanzo Oct 09 '18 at 17:00
  • I'd go ahead and refractor the code. It's going to save you a lot of pain for anything else you'll be doing (just think: listing actor names, any kind of listing really). With a decent IDE that can do refactoring it shouldn't be too hard I'd hope. – fjc Oct 09 '18 at 17:34
  • I'm going with your choice of instantiating each actor for now, and I'll create an issue for the needed refactor. Thanks a lot!!! – Alf Sanzo Oct 09 '18 at 18:05
  • What do you mean by compile time? – Aluan Haddad Sep 12 '20 at 18:46
  • @AluanHaddad the time frame when the compiler compiles. – fjc Sep 14 '20 at 11:50
  • JavaScript is a load and go language. There aren't any language features that are based on compile time code generation or compile time member resolution everything happens at runtime. – Aluan Haddad Sep 14 '20 at 13:07
  • Also, when you say _"During runtime, you have no way of referencing an object's class and hence no access to static fields if you only have an object, but not a type to reference."_, what you're saying isn't accurate. Static members are absolutely available at runtime time and at no other time, they aren't referenced through a type because classes aren't types. It almost sounds like you're talking about a language like C++, and yet even in that language you have access to static members at run time. Please clarify – Aluan Haddad Sep 14 '20 at 13:13
  • @AluanHaddad we are talking about TypeScript here. – fjc Sep 14 '20 at 13:49
  • TypeScript doesn't provide behavior. It provides formalization and tooling via type information. `class A {static m() {}}` in TypeScript means the same thing, and has the exact same capabilities as `class A {static m() {}}` in JavaScript. What TypeScript does is infer two types that represent the type of the `A` object itself and the type of its instances. It uses these to facilitate and validate the use of `A`. That is what TypeScript does. – Aluan Haddad Sep 14 '20 at 14:02
  • Thanks. So can you provide a wording that would be more accurate from your pov? – fjc Sep 14 '20 at 16:27
  • Also, what would a notation be that you would suggest? `this.constructor._image`? – fjc Sep 14 '20 at 16:33
  • Yes. It is a bit awkward to make TypeScript aware of it, as you can see from Matt McCutchen's answer, but it works. It is even polymorphic since everything in JavaScript is latebound. Regarding wording, I would just remove that paragraph. – Aluan Haddad Sep 14 '20 at 18:48
  • Thanks for that – I just removed my entire outdated part of the answer. – fjc Sep 15 '20 at 14:04
3

FTR, you can access the class of the current object. It's called constructor, not class, and you'll need to declare it so that it has a more useful type than Function.

class Actor {
    static _image: string; // I need it to be static

    // Keep static members, remove construct signature because
    // subclasses may define constructors with different parameters. 
    "constructor": Pick<typeof Actor, keyof typeof Actor>;

    show(){  // I need it to be non-static
        this.setImage(this.constructor._image);
    }
}

class GoodActor extends Actor {
    static _image = 'good.png'
}

class BadActor extends Actor {
    static _image = 'bad.png'
}

class MediumActor extends Actor {
    static _image = 'medium.png'
}
Matt McCutchen
  • 28,856
  • 2
  • 68
  • 75