Description
Suppose you have an abstract class A
and two concrete clases B
, C
derived from A
, and you want to store B
and C
as first-class values to make instances from these values:
abstract class A { a: any}
class B extends A { b: any}
class C extends A { c: any}
let classes = [B, C] // inferred type = (typeof B | typeof C)[]
let instance = new classes[1]() // inferred type = (B | C)
instance.a // ok, because .a is in B and C
It will be nice if the previous example inferred the instance
type as A
instead of (B | C)
. But it works.
The problem comes when TS can't infer the classes
type, like when using a Map
instead of an array:
let classes2 = new Map([["b", B], ["c", C]]) // error TS2453: The type argument for type parameter 'V' cannot be inferred from the usage.
let classes3 = new Map<string, typeof A>([["b",B], ["c", C]])
let classes4 = new Map<string, typeof B | typeof C>([["b",B], ["c", C]])
var instance3 = new (classes3.get("c"))() // TS2511: Cannot create an instance of the abstract class 'A'.
var instance4 = new (classes4.get("c"))() // inferred type = (B | C)
classes2
doesn't work because TS can't infer the type.
classes3
doesn't work because you can't use it for instancing.
classes4
example worked but is very clumsy for a large number of classes, you need to type (typeof B | typeof C | ...)
for all the classes involved. Maybe this is acceptable but I think there is a need to be able of defining (and inferring) a type like "all concrete classes derived from A".
Below is the same example but using a concrete base class. Note in this case you can use <typeof A>
as the type of the array / map values:
class A {a : any} // now concrete
class B extends A { b: any}
class C extends A { c: any}
let classes0 = [B, C] // inferred type = (typeof B | typeof C)[]
let classes1 = <typeof A[]>[B,C]
let instance1 = new classes0[1]() // inferred type = (B | C)
instance1.a // ok, because .a is in B and C
let instance2 = new classes1[1]() // inferred type = A
instance2.a // ok, because .a is in A
let classes2 = new Map([["b", B], ["c", C]]) // error TS2453: The type argument for type parameter 'V' cannot be inferred from the usage.
let classes3 = new Map<string, typeof A>([["b",B], ["c", C]])
let classes4 = new Map<string, typeof B | typeof C>([["b",B], ["c", C]])
var instance3 = new (classes3.get("c"))() // inferred type = A
var instance4 = new (classes4.get("c"))() // inferred type = (B | C)
TypeScript Version:
1.9.0-dev.20160616-1.0