Search code examples
typescripttypestypechecking

Type checking in hierarchy: getParentOfType()


I'm still having a hard time with the type checking system of TypeScript. Suppose a composite that holds a collection of elements which all derive from a common base class. How can I implement a function that recursively goes up the hierarchy and returns the first anchestor of a given type?

abstract class Employee
{
    public Superior: Employee;

    /** THIS IS NOT WORKING */
    public getSuperiorOfType<T extends Employee>( type: typeof T ): T
    {
        if (this instanceof T) return this;
        else if (this.Superior !== undefined) return this.getSuperiorOfType(type);
    }
}

class Manager extends Employee {}
class TeamLead extends Employee {}
class Developer extends Employee {}

let tom = new Manager();
let suzanne = new TeamLead();
let ben = new Developer();

ben.Superior = suzanne;
suzanne.Superior = tom;

let x = ben.getSuperiorOfType( Manager ); // x = tom

Thanks in advance for any help...


Solution

    1. The type of 'class type' can not be declared as typeof T. typeof in type position is applicable only to variables, not types. You need to use so-called constructor signature for that:

      public getSuperiorOfType<T extends Employee>(type: { new(...args: any[]): T}): T
      
    2. You can't check if an object is instanceof of a generic type parameter - instanceof T does not work because generic type parameters do not exist at runtime. But you have type as actual function parameter, so instanceof type should work.

    3. There is no real recursion in your code - you are always calling getSuperiorOfType for the same this object. You need to call it as this.Superior.getSuperiorOfType(...) to move up one step in hierarchy.

      abstract class Employee
      {
          public Superior: Employee;
      
          public getSuperiorOfType<T extends Employee>(type: { new(...args: any[]): T}): T
          {
              if (this instanceof type) return this;
              else if (this.Superior !== undefined) return this.Superior.getSuperiorOfType(type);
          }
      }
      
      class Manager extends Employee {}
      class TeamLead extends Employee {}
      class Developer extends Employee {}
      
      let tom = new Manager();
      let suzanne = new TeamLead();
      let ben = new Developer();
      
      ben.Superior = suzanne;
      suzanne.Superior = tom;
      
      
      let x = ben.getSuperiorOfType(Manager); 
      console.log(x === tom); // true