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.
- How can I pass a generic type to
thnorelsfunction param which can accept afunctionorany other type, so that the type checking is strict andreturnsthe correct type? - How can I remove
anytype 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)
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.
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>
}
So the resulting return type is union of both possible branches.
