Rudolf Gröhling
Rudolf Gröhling

Reputation: 4825

Declare (define) Class member methods

I have TypeScript ES6 Class, suppose it looks like this

class Classic
    private _member: Object;
    constructor(member: Object) {
       this._member = member;
    }
}

And I have some rich object, that contains a lot of methods, which I want to inherit, but that object is not a TS or ES6 Class, it's just POJO (ObjectLiteral).

So I made quick and dirty inheritance like

Object.assign(Classic.prototype, ObjectLiteralWithMethods);

No need for deep copy, everything works.

How can I tell TypeScript that Classic has these inherited methods and specify their behavior?

I searched for keywords like declare OR define AND class AND method, but my google-fu is not sufficient.

I tried define interface for Classic, but it did not work either.

I tried to declare class like

declare class ObjectLiteralWithMethods {
    public method(): void;
    protected _method(parameter: string): void;
}

and then extend that declaration like

class Classic extends ObjectLiteralWithMethods { ... }

but then TypeScript wants me to call super() in constructor, which fails.

I know that I can implement "dummy" methods on Classic like

class Classic {
    dummyMethodImplementation(param: string): string {
        return param;
    }
}

but this would be painfull and inefficient. I just want to define/declare that class Classic is extended.

SOLVED

Sort of...

The ObjectLiteralWithMethods in my case was actually a prototype for jQuery widget. So, I extended the owner of the prototype.

It needed some patching, because TS was not aware of the owner so I made:

// declaring the properties and methods which will be inherited by my class
declare class JQueryUIWidget {
    public property: string;
    public method(): string;
    ...
}

// must declare a constructor for JQueryUIWidget to be able to extend
// and cast the value to any, so TS do not mind
const Widget: { new (): JQueryUIWidget } = $.Widget as any;

// and then extend that declaration
class Classic extends Widget {
    constructor() {
        super(); // super(); is mandatory here, with plain object it 
                 // did not worked, but since Widget is in fact a Function
                 // it works now
    }
}

Upvotes: 6

Views: 15610

Answers (2)

Matt B
Matt B

Reputation: 8653

A bit late, but I had a similar requirement when converting parts of a legacy system to TypeScript, so this might help somebody else.

If you know your class will be enhanced at runtime with other methods and you simply want to have type information and intellisense available for those methods, AND you don't want to write ugly stubs, you can try this:

First, define your interface(s) detailing the runtime methods:

interface RuntimeImplementedType {
    public method(): void;
}

interface AnotherRuntimeImplementedType {
    public other_method(): void;
}

Then define a variable whose type is specified as a constructor function returning the union of the types that you want to inform TypeScript your class implements at runtime. Assign the variable a function returning an empty object literal, casting to "any" so as to avoid compiler grumbles and ensure that when transpiled, what is actually being extended is just a plain old empty object.

const Base: new() => RuntimeImplementedType & AnotherRuntimeImplementedType = (() => {}) as any;

Now define your new class, extending from your constructor function variable:

class Classic extends Base {
    public doSomething() {
        this.method();
        this.other_method();
    }
}

All methods from the interfaces should be available, but without having to actually provide an implementation.

Upvotes: 3

Nitzan Tomer
Nitzan Tomer

Reputation: 164417

You can do this:

declare class ObjectLiteralWithMethods {
    public method(): void;
    public _method(parameter: string): void;
}

class Classic implements ObjectLiteralWithMethods {
    private _member: Object;

    constructor(member: Object) {
       this._member = member;
    }

    method: () => void;
    _method: (parameter: string) => void;
}

(code in playground)

But you won't be able to use protected members/methods in ObjectLiteralWithMethods because you are using it as an interface.


Edit

If you need to override methods in Classic in a subclass then you'll need to use stabs for the methods:

class Classic implements ObjectLiteralWithMethods {
    private _member: Object;

    constructor(member: Object) {
       this._member = member;
    }

    method() {};
    _method(parameter: string) {}
}

class Other extends Classic {
    method() {

    }
}

The methods in Classic are empty, and you override them with the implementation in ObjectLiteralWithMethods.

Upvotes: 0

Related Questions