Reputation: 1538
I have an abstract error class for service errors to inherit, to make error handling a bit easier. (ErrorBase is from https://gist.github.com/justmoon/15511f92e5216fa2624b#gistcomment-1928632)
export abstract class ServiceError extends ErrorBase {
abstract statusCode(): number;
abstract readonly errorCode: string;
static readonly defaultMessage: string;
constructor(readonly context?: any, message?: string) { super(message); }
toJSON(key: any) {
return {
errorCode: this.errorCode,
message: this.message,
context: this.context
};
}
}
Here's an example of a class which extends it:
export class ExampleError extends ServiceError {
statusCode(): number { return 400; }
errorCode: string = "err-example";
static readonly defaultMessage = "This is an example error";
constructor(context?: any, message?: string) {
super(context, message ? message : ExampleError.defaultMessage);
}
}
I've been trying to figure out a way to access the defaultMessage of the inherited class from inside of the base class's constructor so that I can simplify the constructors of my inherited classes. Is there any way I can do this?
Upvotes: 5
Views: 3368
Reputation: 27264
Greg's answer is correct, you should be using this.constructor
. You have to cast it as typeof ServiceError
because of the reasons explained in this issue. It's pretty complex but the short version is that you can't say anything in the base class about the shape of the constructor function for subclasses -- Base
could have constructor()
while Sub
has constructor(string, number)
-- and that means that this.constructor
has to type as Function
until they can come up with a better idea.
It works because static assignments run from base class, then child class, before any constructors are invoked, then the subclass constructor is invoked but it must immediately call its parent constructor. All the parent's property assignments run before the constructor body, then the parent constructor runs, then we're back in the child constructor after the super()
call.
If I understood your question correctly, this should do what you're asking for:
abstract class ServiceError {
static defaultMessage = "Base default";
constructor(message?: string) {
if (!message) { message = (this.constructor as typeof ServiceError).defaultMessage; }
console.log(message);
}
}
class ExampleError extends ServiceError {
static defaultMessage = "Sub default";
constructor(message?: string) {
super(message);
}
}
let a = new ExampleError("Explicit"); // Logs "Explicit"
let b = new ExampleError(); // Logs "Sub default"
Upvotes: 1
Reputation: 2085
Static properties are just properties defined on your prototype function. They compile like so:
// TypeScript
class A {
public static property = 'my string';
}
// Compiled ES5 JavaScript
function A() {}
A.property = 'my string';
Therefore, you can use the constructor
property on this
to access the derived class/function that being used to construct the instance (or rather copied):
class A {
public static defaultMessage = 'My Error A';
constructor() {
// will output "My Error B" if a instance of B is being constructed
console.log(this.constructor['defaultMessage']);
}
}
class B {
public static defaultMessage = 'My Error B';
}
Upvotes: 4