Home > Mobile >  Typescript: trying to replace ternary operator with functional implementation
Typescript: trying to replace ternary operator with functional implementation

Time:10-26

I am trying to replace a ternary operator with functional implementation. Finding it hard to write the typescript type for the below code where any is written.

  1. How can I pass a generic type to thn or els function param which can accept a function or any other type , so that the type checking is strict and returns the correct type?
  2. How can I remove any type in the below code with correct types?
interface Predicate {
    (...args: any): boolean;
}

const ifThenElse = (bool: boolean | Predicate) => (thn: any) => (els: any) : any => {
 if(bool) {
   if(typeof thn === 'function') {
     return thn()
   }
   return thn
 }
  if(typeof els === 'function') {
     return els()
   }
   return thn
}

var coffeesToday = ifThenElse(true)(3)(1);
var coffeesTomorrow = ifThenElse(false)(() => 3)( () => 4);
console.log('coffeesToday', coffeesToday)
console.log('coffeesTomorrow', coffeesTomorrow)

Playground

CodePudding user response:

Typescript is smart about many things, but there are cases when it cannot infer what types are possible.

const value = (maybeValue: any | Function) =>
  typeof maybeValue === "function" ? maybeValue() : maybeValue;

const ifThenElse = <T>(
  predicate: boolean | Predicate,
  thn: T | (() => T),
  els: T | (() => T)
): T => {
  return value(predicate) ? value(thn) : value(els);
};

I changed your function to take all three params in one function call. In this situation Typescript can tell (infer) that the type to return T is a number. When you call the function the parameters are not changing later.

That is not true with your original curried function.

E.g. calling ifThenElse2(false) below is perfectly valid but we cannot infer the value of T yet. This is your original version (with my modifications):

const ifThenElse2 =
  <T>(predicate: boolean | Predicate) =>
  (thn: T | (() => T)) =>
  (els: T | (() => T)): T => {
    return value(predicate) ? value(thn) : value(els);
  };

We can help Typescript by telling what T is.

var coffeesToday2 = ifThenElse2<number>(true)("foo")(1);

The above will not compile since ifThenElse2 now expects T to be numbers.

Playground

CodePudding user response:

Here's what you could do:

type Result<T> = T extends (...args: any[]) => infer R ? R : T

const ifThenElse = (bool: boolean | Predicate) => <T>(thn: T) => <E>(els: E): Result<T> | Result<E> => {
  if (bool) {
    if (typeof thn === 'function') {
      return thn()
    }
    return thn as Result<T> | Result<E>
  }
  if (typeof els === 'function') {
    return els()
  }
  return els as Result<T> | Result<E>
}

Playground

So the resulting return type is union of both possible branches.

  • Related