Reputation: 1817
Let's consider Opt
from ts-opt package (it's just a Maybe/Option/Optional) and isNumber
to have a type guard ((x): x is number => ...
).
Is it possible to implement (and how) method someIf
in a generic way (working with arbitrary type guard, type-safe, without user needing to specify type which is already in a type guard):
type A = number | string;
const x: A = 4;
const y: A = 'y';
opt(x) // Opt<number | string>
.someIf(isNumber); // Opt<number> (Some(4))
opt(y) // Opt<number | string>
.someIf(isNumber); // Opt<number> (None)
Upvotes: 2
Views: 69
Reputation: 15313
One way of achieving that (without having someIf
as a prototype function) is the following
type A = number | string;
const x: A = 4;
const y: A = "y";
const isNumber = (a: any): a is number => typeof a === "number";
const someIf = <T>(ot: Opt<any>, f: (x: any) => x is T): Opt<T> =>
ot.caseOf(
t => (f(t) ? opt(t) : none),
() => none
);
const ox = someIf(
opt(x),
isNumber
); // Opt<number> <- Some(4)
const oy = someIf(
opt(y),
isNumber
); // Opt<number> <- None
The idea is pretty simple. Fold over the optional value, if none
, we have nothing to do. Otherwise, validate the wrapped value against the type guard. If the guard succeeds, simply return the value (with its type now narrowed down), else return none
.
Upvotes: 1