Is there a way to use mixin classes as classes instead of as values?

4 days ago 9
ARTICLE AD BOX

I'm following the example in TypeScript's mixin documentation, which promises me that:

With these all set up, then you can create a class which represents the base class with mixins applied:

class Sprite {...} function Scale<TBase extends new (...args: any[]) => {}>(Base: TBase){...} // Compose a new class from the Sprite class, // with the Mixin Scale applier: const EightBitSprite = Scale(Sprite);

Except, I'm finding that EightBitSprite is actually a value rather than a class i.e:

const mapOfSprites = new Map<string, Sprite>(); //Works //TS2749: EightBitSprite refers to a value, but is being used as a type here. Did you mean typeof EightBitSprite? const mapOfEightBitSprites = new Map<string, EightBitSprite>();

The annoying thing about this is that:

Everywhere in my code, when using a class's type, I need to remember if it's a real class or a mixed class.

If I change a class declaration to a mixed class, then I need to update all the places I use its type. E.g., if I change class ScalableTextBox {...pre-mixin-impl...} → const ScalableTextBox = Scale(TextBox) then I need to go update all function(): ScalableTextBox {...} to function(): typeof ScalableTextBox {...} and so on.

The only workaround I've found so far is to define a third class that extends the result of mixing the base class, i.e.

//Works class RealEightBitSprite extends EightBitSprite {} const mapOfRealEightBitSprites = new Map<string, RealEightBitSprite>();

This is OK. It solves problem #2 (E.g. class ScalableTextBox {...} → class ScalableTextBox extends Scale(OldScalableTextBox){}. However:

It feels clunky / inelegant to me. Maybe because it results in a rather convoluted calss hierarchy: RealEightBitSprite extends EightBitSprite extends Scaling extends Sprite

More importantly, problem #1 has gotten worse. I now have two different 'classes', RealEightBitSprite and EightBitSprite that are functionaly identical, and so are easy to mix up. The only noticable diference between them is that RealEightBitSprite is both a type and a value, whereas EightBitSprite is a value only.

Is there a way to make it so that EightBitSprite is both a value and a type? The documentation for Declaration Merging implied that there isn't, and sent me back to Mixins, so I suspect that there just isn't a solution.

Read Entire Article