0

Say that I have declared my classes and types as below

declare type Class<T = any> = new (...args: any[]) => T

export class BaseIntersectHandler<UuidException extends Class<Error>>
{
    constructor(private uuid_exception: UuidException) { 
         // implementation
    }
    debugMethod(): void {
        throw new this.uuid_exception("debug string")
    }
}

export class ClickHandlerUuidException extends Error
{
    // implementation
}

export class ClickHandler extends BaseIntersectHandler<ClickHandlerUuidException>
{ 
     constructor() {
          super(ClickHandlerUuidException)
     }
}

const sampleInstance = new ClickHander()
sampleInstance.debugMethod() // should throw the error from base method

Implementing above gives the error below when compiling:

      TS2344: Type 'ClickHandlerUuidException' does not satisfy the constraint 'Class<Error>'.
  Type 'ClickHandlerUuidException' provides no match for the signature 'new (...args: any[]): Error'.

I'm trying to be specific about UuidException generic being inherited from Error in JS. How should I change this? I'm sure this is a duplicate, but any help is appreciated!

2 Answers 2

1

I fixed the above issue by removing the type Class and work directly with Error as below:

export class BaseIntersectHandler<UuidException extends Error>
{
    constructor(private uuid_exception: { new(...args: any[]): UuidException } ) { 
         // implementation
    }
    debugMethod(): void {
        throw new this.uuid_exception("debug string")
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

You most obviously violate the LSP: your base class has a constructor that can take any number of arguments, but your derived class has a constructor that accepts exactly no arguments. The compiler duly complains.

There are two obvious ways out.

The traditional OOP way is a protected constructor and a factory method.

class Base {
  protected constructor(...args: any[]) { ... }  // Not publicly visible.
  // NOTE: this class is an abstract base, so it cannot be instantiated.
}

class Derived extends Base {
  // The signature of the constructor is the same, but nobody can misuse the args.
  protected constructor(...args: any[]) { 
    super("Whatever", "It", "Needs");
    // Some more initialization. 
  }
  create() { return new Derived(); }  // Can call the constructor.
}

class AlsoDerived extends Base {
  // Or use the inherited base constructor directly.
  create(color: string) { return new AlsoDerived("Color", color); }
}

// This works:
Derived.create();
AlsoDerived.create("red");

// Won't work:
new Derived();

A better way would be to define an interface, and avoid inheritance.

Since a constructor is not a part of the interface, it can have any signature a particular class needs.

interface Debuggable {
  debugMethod(): void;
}

class A implements Debuggable {
  constructor(a: number, b: string) { ... }
  debugMethod() { console.log("This is A"); }
}

class B implements Debuggable {
  constructor() { ... }
  debugMethod() { console.log("This is B"); }
}

But these classes cannot inherit from one another. Some traditional OOP approaches rely heavily on having large trees of derived classes, and on using a few classes closest to the root for type declaration. This usually can be replaced by defining several interfaces, and making particular classes implement some of them, often more than one.

This approach avoids typical bugs (forgot to override a method, the inherited method wreaks havoc) and gives much more flexibility. It takes some getting used to, though.

3 Comments

Thank you, but I can't see how it relates to my problem. Could you show how this work with a class inheriting Error. I somewhat solved this issue by replacing the type of private uuid_exception in BaseIntersectHandler with private uuid_exception: { new(uuid: string): UuidException } and the generic signature with UuidException extends Error.
This directly relates to your problem. The problem, to my mind, is in the very first line of your source, where you insist that constructors must have signature (...args: any[]). There's no way around that: either you give this signature to constructors of every derived class, or you drop that requirement and pick one of the ways allowing for different constructor signature.
I have added the workaround I use. Please let me know if you believe it is inadequate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.