Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declare an additional member to a method of a class

I have decorated a method with a typescript decorator adding a member to the method:

export class MyClass {
    @AddMyMember()
    public myMethod(param1:string) {
        // ...
    }
}

So that I should be able to use it this way:

const myClassInstance = new MyClass();
myClassInstance.myMethod.myMember(); // error of course here, as myMember has not been declared yet...

But I cannot find a way to declare myMember efficiently:

export declare interface MyClass {
// Something great here maybe?....
}
like image 989
Charles HETIER Avatar asked Nov 06 '18 14:11

Charles HETIER


People also ask

How do you declare a class member in Java?

A class member is declared with an access modifier to specify how it is accessed by the other classes in Java. A Java class member can take any of the access modifiers, such as - public, protected, default and private.

How do you declare a method in Java?

In the method declaration, you specify the type followed by an ellipsis (...). Only one variable-length parameter may be specified in a method, and this parameter must be the last parameter. Any regular parameters must precede it.

What is Member and methods of class?

A "class member" is basically anything that can be contained within a class object. A "method" is a particular type of member, described by MSDN as such: Methods define the actions that a class can perform.

Can we create a member variable in a class?

You declare a class's member variables with the body of the class. Typically, you declare a class's variables before you declare its methods, although this is not required. Note: To declare variables that are a member of a class, the declarations must be within the class body, but not within the body of a method.


1 Answers

The merging problem

There is no way to declare a member of a class with a different type, declaration merging can only add members not change the types of existing members.

Something we can do is use an intersection type. If two members of an intersection type have the same name and different types the member on the intersection will be an intersection of the two original types.

class _MyClass {
    public constructor (t: string) {

    }
    public myMethod(param1:string) {
    }

    static staticF(){}
}

type ReplaceInstanceType<T extends new(...a: any[])=> any, TNew> = {
    new (... a: (T extends new (...p: infer P) => unknown ? P: [])) : TNew
} & Pick<T, keyof T>

export type MyClass = _MyClass & {
    myMethod: {
        myMember(): void
    }
}


export const MyClass: ReplaceInstanceType<typeof _MyClass, MyClass> = _MyClass as any;

let a = new MyClass("") // can new up
a.myMethod("") // can call myMethod
a.myMethod.myMember(); // can call  myMember
MyClass.staticF

The decorator problem

The second part of your problem is that a decorator can't mutate the type of the member it is applied to (this is by design). You could use a HOC to create the class instead, and pass in a list of member names (compile checked using keyof) to have the extra members added :

type ReplaceInstanceType<T extends new(...a: any[])=> any, TNew> = {
    new (... a: (T extends new (...p: infer P) => unknown ? P: [])) : TNew
} & Pick<T, keyof T>

function addMemebers<T extends new(...a: any[])=> any, K extends keyof InstanceType<T>>(members: K[], cls: T) : ReplaceInstanceType<T, InstanceType<T> & Record<K, {myMember(): void}>>{
    return null as any; // replace with implementation
}
export const MyClass = addMemebers(["myMethod"], class {
    public constructor (t: string) {

    }
    public myMethod(param1:string) {
    }

    static staticF(){}
})


let a = new MyClass("") // can new up
a.myMethod("") // can call myMethod
a.myMethod.myMember(); // can call  myMember
MyClass.staticF
like image 50
Titian Cernicova-Dragomir Avatar answered Sep 20 '22 02:09

Titian Cernicova-Dragomir