You say you don't want to declare member variables like func1Count, etc, but that really is the most straightforward way someone would do something like this, especially because it uses properties and methods the way the TypeScript compiler understands. If you can explain why you don't want to use member variables, it's possible someone could come up with an implementation which meets your needs and doesn't have to jump through too many hoops.
For now I'll assume you really need to add a property to a method (and not a standalone function, support for which was added in TS3.1). TypeScript doesn't generally allow expando properties; if you want to add a property to something, its type needs to be known to have that property.
The easiest way to do this for something like a function which can't be created via an object literal is with Object.assign(). Something of the form
const funcWithProp = Object.assign(function(){}, {prop: ""});
will be inferred to have the type
// const funcWithProp: (() => void) & { prop: string; }
and can be used both as a function and as a prop-keyed object:
funcWithProp(); // okay
funcWithProp.prop = "hey" // okay
So, let's look at your class and see what to do:
I'm not sure whether you want to have a single counter that increments whenever any instance of MyError has its log() method called, or if you want a separate counter for each instance of MyError. So I'll implement both.
If you want a single counter, you should do it like this:
class MyError {
message: string;
constructor(message: string) {
this.message = message;
}
}
interface MyError {
log: ((this: MyError) => void) & { counter: number };
}
MyError.prototype.log = Object.assign(
function(this: MyError) {
this.log.counter++;
console.log(`${this.log.counter} | ${this.message}`);
},
{ counter: 0 }
);
Note that I had to use declaration merging and set log directly on MyError.prototype. Methods usually end up on the prototype (there's only one of them per class), and when you declare a method inside a class, there's no way to annotate that you'd like it to have more properties. So the proper annotation needs to go inside interface MyError, which is merged into the class, and the actual implementation is assigned to MyError.prototype.log and uses Object.assign(). Also notice that the log function signature features a this parameter. That is just part of the type system, and it lets the compiler know that you can only call the function as a method on an object of the MyError type, and that the implementation of the function can access this as an instance of MyError.
Let's see if it works:
const myError = new MyError("Some Message");
myError.log(); // 1 | Some Message
const myError2 = new MyError("Other Message");
myError.log(); // 2 | Some Message
myError2.log(); // 3 | Other Message
myError.log(); // 4 | Some Message
Looks good.
What if you want each MyError instance to have its own log counter? We'd do it this way:
class MyError {
message: string;
constructor(message: string) {
this.message = message;
}
log = Object.assign(
function(this: MyError) {
this.log.counter++;
console.log(`${this.log.counter} | ${this.message}`);
},
{ counter: 0 }
);
}
Now we can't use a normal method that goes on the prototype, or there'd be only one of them for the whole MyError class. You want to keep track of each instance's call to log(), meaning you need separate counter properties for each instance. But you can only do that if you have separate log() implementations for each instance... (well, unless you use MyError member variables, but you don't want to do that). So instead of making log a method, we make it a function-valued instance property, initialized with =. We use the same Object.assign(...) code from before, and it just works.
Well, let's see if it works:
const myError = new MyError("Some Message");
myError.log(); // 1 | Some Message
const myError2 = new MyError("Other Message");
myError.log(); // 2 | Some Message
myError2.log(); // 1 | Other Message
myError.log(); // 3 | Some Message
Looks good.
All right, hope that helps; good luck!
Link to code
const myError = new MyError('Some Message'); myError.log(); const myError2 = new MyError('Other Message'); myError.log(); myError2.log(); myError.log();You might be surprised. What are you trying to count?MyError? Or are you trying to keep a separate count for each instance ofMyError? Either way the code you have doesn't quite do it.