1

Consider this example:

class A {}
class B {}
type AB = A | B
// type ABT = typeof AB // this does not work, error "AB is used as a value here"
type ABT = typeof A | typeof B

Is it possible to distribute typeof over the union without repeating the members?

esp
  • 7,314
  • 6
  • 49
  • 79
  • 1
    `typeof` doesn't mean "give me a constructor for this instance type"; it means "give me the type of this value". There is a *value* named `A` that exists at runtime which is a constructor of instances of type `A`, and there is a *value* named `B` that exists at runtime which is a constructor of instances of type `B`. So `typeof A` is the type of the `A` constructor and `typeof B` is the type of the `B` constructor. There is no *value* named `AB` that exists, unless you also write something like `const AB = Math.random()<0.5 ? A : B;` so there is no `typeof AB`. Why do you need this? – jcalz Sep 28 '20 at 19:15
  • 3
    There's also [this answer](https://stackoverflow.com/a/50396312/2887218)... I'm kind of inclined to close this as a duplicate of that, although that is specifically about enums and not classes. Both enums and classes have the same feature that make this confusing though: they introduce a value and a type of the same name but different meanings. – jcalz Sep 28 '20 at 19:19
  • I understand about the constructors and their types. I am passing them as function parameters to check other instances against them (with instanceof) and I was hoping to have a more concise type definition for these parameters. They have a common subclass, but I cannot just use typeof Subclass as parameter type - it is not polymorphic... What would also help is a polymorphic type of constructor that allows types of subclasses. – esp Sep 29 '20 at 06:03

1 Answers1

1

If you're simply wanting a constructor type, you can use this:

type Constructor<T> = new (...args: unknown[]) => T
type ABT = Constructor<AB> // new (...args: unknown[]) => AB

You can use this with instanceof and it also allows subclasses:

const something = (Class: ABT, object: unknown) => {
  if (object instanceof Class) {
    object // type AB
  }
}

class C extends A {}
something(C, {})

However, if the class has static members, this is not the same as typeof:

class D {
  static x = 0
}
declare const typeofD: typeof D
declare const constructorD: Constructor<D>

new typeofD()
new constructorD()

typeofD.x
constructorD.x // Property 'x' does not exist on type 'Constructor<D>'.

Playground link

Lauren Yim
  • 12,700
  • 2
  • 32
  • 59
  • In my case they actually did have a common static member, but I made Constructor an interface and it worked (In my case I didn't even need a parameter, it was a specific list of classes). Another difference is that it doesn't work for abstract classes - i.e., I can pass the constructor of an abstract class to `typeof T` parameter, but not to `Constructor` and there doesn't seem to be a way to generalize it to allow abstract classes - so I had to make one parent class non-abstract - still better than to have a long list of typeofs. Thank you. – esp Oct 02 '20 at 06:25