Reputation: 118
In my TypeScript program I want to extend a class that is declared in a library. The trick is that the library doesn't "export" it so I can not access it directly. Instead, it provides a builder function, like this:
export namespace Library {
class BaseUnexported { // no export here, for some reason
public foo() { console.log("foo"); }
}
export function buildUnexportedInstance(): BaseUnexported {
return new BaseUnexported();
}
}
I'm trying to extend the class like this:
import { Library } from "./library";
export default class Derived extends Library.BaseUnexported {
public bar() { console.log("bar"); }
}
This would work if the library had "exported" the class definition; but without export I get error TS2339: Property 'BaseUnexported' does not exist on type 'typeof Library'.
I tried to obtain the type from the constructor function like:
type BaseType = ReturnType<typeof Library.buildUnexportedInstance>
export default class Derived extends BaseType {
And this time getting error TS2693: 'BaseType' only refers to a type, but is being used as a value here.
So, my question is: is there a way to extend class that is declared without "export" keyword? Maybe some prototype-based magic? Note that my goal is to create a new class, I want to leave the original class unchanged.
P.S. This is a simlified example; in fact I'm trying to extend widgets from a great library called blessed. Just want to create my own widgets which would extend functionality of existing ones.
Upvotes: 3
Views: 1708
Reputation: 14620
Simple answer, you can't do what you want to do. It almost looks as if the factory exists purely to stop you extending the base class. This is a reasonable thing to do in my eyes as it most certainly stops bugs created by your own code that would manifest in the base class.
You have some options, the one I'd least recommend would be forking the project and exposing the class to extension. This is undesirable in my eyes as you open yourself up to potentially tedious manual updates and it gives you impetus to start messing with closed (and likely heavily tested) code.
Instead I'd look toward composition over extension as a viable (and potentially better) alternative such as decorating the base class or using your own builder/director to create widgets that use an instance of the baseclass but with your own functionality on top.
What you won't be able to do is modify the base instances methods or non public internals, this may or may not be a sticking point for you.
I don't know the library you're using and this may not be possible (after all that), it depends on what the output of the different library methods are but, for example I may look to try:
// As the type doesn't exist in scope you could expose
// it yourself with a custom `.d.ts`.
class MyWidgetLibrary implements BaseLibrary {
private library: BaseLibrary;
constructor(library: BaseLibrary) {
this.library = library;
}
// for example create decorator methods to wrap the base widgets
}
Upvotes: 2