Skip to content

Typing is clumsy when instancing using class objects derived from an abstract class #9302

Closed
@nahuel

Description

@nahuel

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions