Search code examples
classtypescriptprototype

How to check if key in Interface is a class that extends a specific Parent class?


In typescript, for an interface, is there a way to enforce the type of a property to be the "child subclass C, that extends class P"?

example.ts

import { P } from '/path/to/types'
class C extends P {
   ...
}

types.ts

// `C` is not accessible here
class P {
   ...
}


interface {
    myProp: ???? <-- how to enforce `myProp` is a subclass of P (class, not instance)?
}

Alternatively, I can check Object.getPrototypeOf(myProp.prototype) === P.prototype, but that is at runtime. Is there a way to enforce this condition via types during compile/checker time?


Solution

  • You can use typeof to get the type:

    class P { ... }
    interface Foo {
        myProp: typeof P
    }
    //Usage
    class C extends P { ... }
    class D { ... }
    //Ok
    var f: Foo = {
        myProp: C
    }
    // Error
    var f2: Foo = {
        myProp: D
    }
    

    This will ensure that the class specified as the property will be compatible with P.

    Edit

    Since Typescript uses structural compatibility, any class that has the same structure as P will be compatible with typeof P. If P has only public members and a small number of members, there may be a risk that another class may unwittingly have the same structure. To ensure that only classes directly inheriting P are compatible, you can add a private member to P that makes P incompatible with other classes (even ones with the same structure, different declarations of private fields are not compatible with each other)

    class P { private nonStructural:true }
    interface Foo {
        myProp: typeof P
    }
    //Usage
    class C extends P { }
    class D { private nonStructural:true }
    //Ok
    var f: Foo = {
        myProp: C
    }
    // Error
    var f2: Foo = {
        myProp: D
    }