menfon
menfon

Reputation: 1817

How to narrow a generic type via passed guard function?

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

Answers (1)

bugs
bugs

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

Sandbox link


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

Related Questions