Home > Blockchain >  Typescript class method return type based on parameter
Typescript class method return type based on parameter

Time:02-22

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'!

CodePudding user response:

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.

CodePudding user response:

I actually found the answer I was looking for on how to use conditional typing on a class method with an interface.

It seems that you can use the interface with its conditional generic, without creating all the possible overloads, as long as you type the returned values from the implementation.

So according to my original post, this would be:

type ResType<T extends boolean> = T extends true ? string : boolean;

interface common3 {
  test<T extends boolean>(flag: T): ResType<T>
}

class Exp3 implements common3 {
  test<T extends boolean>(flag: T): ResType<T> {
    if (flag) {
      return 'truthy' as ResType<T>;
    } else {
      return false as ResType<T>;
    }
  }
}


const val3 = new Exp3().test(false); // boolean
const val4 = new Exp3().test(true); //  string

Typescript Playground

  • Related