14

What type should I use in typescript to represent any class?

I'm trying to write a function that takes an array of classes and returns an array with different order.

function shuffle(classes: typeof Object[]) : typeof Object[] {
    return ...;
}

class A { }
class B extends A { }
class C extends B { }
class D extends B { }
shuffle([A, B, C, D]);

Argument of type 'typeof A[]' is not assignable to parameter of type 'ObjectConstructor[]'.

Then I've tried:

shuffle([typeof A, typeof B, typeof C, typeof D]);

error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'ObjectConstructor[]'. Type 'string' is not assignable to type 'ObjectConstructor'.

What's the right way? Generics? How? This doesn't work:

export function <T extends typeof Object> shuffle(classes: T[]) : T[]

This neither:

export function <T extends Object> sortClassesBySpeciality(classes: typeof T[]) : typeof T[]

Also why typeof (typeof A) is "string" and "" + typeof A is function? Ok, got this, typeof has two very different meanings context of type definition and expression.

(The ultimate goal is to sort the classes by level of extends from Object.)

1

1 Answer 1

12

You should avoid using the type Object in typescript, you better use any as the docs say:

You might expect Object to play a similar role, as it does in other languages. But variables of type Object only allow you to assign any value to them - you can’t call arbitrary methods on them, even ones that actually exist

But if you want to represent classes then you need to have the following form:

{ new (): CLASS_TYPE }

Or in your case:

function shuffle(classes: Array<{ new (): any }>): Array<{ new (): any }> {
    return [];
}

class A { }
class B extends A { }
class C extends B { }
class D extends B { }
shuffle([A, B, C, D]);

(code in playground)

If all of your classes are based on a super class (as your example implies) then you can simply do:

function shuffle(classes: Array<{ new (): A }>): Array<{ new (): A }> {
    return [];
}

Edit

Just saw that you want to

sort the classes by level of extends from Object

To answer that:

function shuffle(classes: Array<{ new (): any }>): Array<{ new (): any }> {
    return classes.sort((a, b) => getInheritanceLevel(a) - getInheritanceLevel(b));
}

function getInheritanceLevel(cls: { new (): any }): number {
    let level = 0;

    while (Object.getPrototypeOf(cls.prototype) !== Object.prototype) {
        level++;
        cls = Object.getPrototypeOf(cls.prototype).constructor;
    }

    return level;
}

shuffle([D, A, C, B]); // returns [A, B, D, C]

(code in playground)

Sign up to request clarification or add additional context in comments.

5 Comments

Am I right that the type syntax means "type(s) whose constructor returns A"? So there's no direct support like Class<T extends A> like in Java? How would this work with interfaces?
It's more like a "type who has a constructor for A", you can also define static class functions there. I don't think that there are plans to make it more like the java way, but what are you missing from the typescript way? It suits what you're after.
Also, it could also be { new (): any }[], is that equivalent?
I've introduced an alias: type AnyClass = { new (): any };
Yeah, should be fine. The convention is to call those constructors, so type AnyConstructor, just like the ArrayConstructor

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.