Typescript: conditional type depending on whether type is undefined

151 views Asked by At

I am creating a series of class which all extend an abstract class Operation<T, R>, on which is defined the method perform(t: T): R.

I want a conditional operation ConditionalOperation<T, R> extends Operation<T, T | R>, with a constructor which signature would be similar to this one:

constructor(
    private predicate: (value: T) => boolean,
    private thenOp: Operation<T, R>,
    private elseOp?: Operation<T, R>
) {
    super();
}

The method perform of this operation would work as follows:

  • If elseOp is defined, the input value is passed to thenOp if the predicate is true, else it is passed to elseOp.
  • If elseOp is undefined, the input value is passed to thenOp if the predicate is true, else the value is returned directly.

This behaviour is the reason for the T | R in the signature of ConditionalOperation: if the elseOp is defined, the return type is R, otherwise it's T | R.

Now, I would like to try and see if there is a way to typecheck the ConditionalOperation to have a return type that depends on whether elseOp is defined. I tried the following:

abstract class Operation<T, R> {
  abstract perform(t: T): R;
}

type ConditionalReturnType<T, R, EOp extends Operation<T, R>> = undefined extends EOp ? T | R : R;

class ConditionalOperation<T, R, EOp extends Operation<T, R>> extends Operation<T, ConditionalReturnType<T, R, EOp>> {
  constructor(
        private predicate: (value: T) => boolean,
        private thenOp: Operation<T, R>,
        private elseOp?: EOp
    ) {
        super();
    }

  perform(t: T): ConditionalReturnType<T, R, EOp> {
    if (this.predicate(t)) {
      return this.thenOp.perform(t);
    } else if (typeof this.elseOp !== 'undefined') {
      return this.elseOp.perform(t);
    } else {
      return t; // Type 'T' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
    }
  }
}

Typescript does not seem to allow returning t here, even though we know at this point that elseOp is undefined, which should be possible if and only if ConditionalReturnType<T, R, EOp> is T | R.

Is there another way of doing this, aside from creating two conditional classes?

0

There are 0 answers