Resolved: Typescript class method return type based on parameter

Question:

I am trying to create a class with a method whose return type will depend on the boolean value of a flag-parameter
I have managed to do this successfully when
  • using a plain function expression that implements an interface,
  • or in a class when declaring the method overloads directly in the class body

However, I am not able to do this when I am trying to use an interface that is implemented by the class.
I would like to do this using interface implementation for a class method to achieve the dynamic return type.
Thanks!
Here is the example code which is also available in the Typescript Playground
// it works with interface for plain function
interface common {
  <T extends boolean>(flag: T): T extends true ? string : boolean ;
  (flag: boolean): string | boolean;
}
const method: common = (flag: boolean) => {
  if (flag) {
    return 'truhty';
  } else {
    return false ;
  }
}

// it works with plain method overload

function test(flag: true): string
function test(flag: false): boolean
function test(flag: boolean): string | boolean {
  if (flag) {
    return 'truthy'
  } else {
    return false;
  }
}

const x = test(true);
const y = test(false);

// This works with direct class method overload

class Exp {
  test(flag: true): string
  test(flag: false): boolean
  test(flag: boolean): string | boolean {
    if (flag) {
      return 'truthy'
    } else {
      return false;
    }
  }
}

const val = new Exp().test(false); // boolean!
const val2 = new Exp().test(true); // string!


// It does not work in class with interface overload
interface common2 {
  test(flag: true): string
  test(flag: false): boolean
  test(flag: boolean): string | boolean
}

class Exp2 implements common2 {
  test(flag: boolean) {
    if (flag) {
      return 'truthy'
    } else {
      return false;
    }
  }
}

// it is not working in class with generic conditional type

interface common3 {
  test<T extends boolean>(flag: T): T extends true ? string: boolean
  test(flag: boolean): string | boolean
}

class Exp3 implements common3 {
  test(flag: boolean) {
    if (flag) {
      return 'truthy';
    } else {
      return false;
    }
  }
}


const val3 = new Exp3().test(false); // WRONG false | 'truthy'!
const val4 = new Exp3().test(true); // WRONG false | 'truthy'!


// it does not work with conditional directly in class
class Exp4 {
  test<T extends boolean>(flag: T): T extends true ? string : boolean
  test(flag: boolean) {
    if (flag) {
      return 'truthy';
    } else {
      return false;
    }
  }
}

const val5 = new Exp3().test(false); // WRONG false | 'truthy'!
const val6 = new Exp3().test(true); // WRONG false | 'truthy'!

Answer:

I don’t think you get a choice but to repeat the overload in the class (as you did with Exp) when implementing the interface; here’s a modified Exp2 that both implements the interface and lists the overloads:
class Exp2 implements common2 {
  test(flag: true): string;
  test(flag: false): boolean;
  test(flag: boolean): string | boolean {
    if (flag) {
      return 'truthy'
    } else {
      return false;
    }
  }
}
Playground example
If you don’t, the class method isn’t a match for the interface definition. The interface is a contract your class must match, not a blueprint that affects how the class’s declarations are interpreted.

If you have better answer, please add a comment about this, thank you!