How to type an input function as a type guard?

225 views Asked by At

How can I generalize a function mapping an array of T1 to a array containing only T2 where T2 is a subtype of T1?

function filterJustStrings(arr: (string| number)[]) {
  return arr.filter((i) => typeof i === 'string') as string[];
}

Given predicate functions which are defined as type guards

function isNumber(i: any) : i is number {
  return typeof i === 'number';
}

function isString(i: any) : i is string{
  return typeof i === 'string';
}

I would like to improve the typing of this function:

function filterJustT<TWide extends T, T>(predicate: (i: TWide) => boolean): (arr: TWide[]) => T[] {
  return (arr: TWide[]) => arr.filter((i) => predicate(i)) as T[];
}

So that the return type is inferred using the guard type

const filterJustStrings2 = filterJustT(isString);
const filterJustStrings3 = filterJustT(isNumber);

The code above infers the expected input correctly based on the input of the predicate, but the return type is unknown[]

Is there a way to extract the guard type narrowing?

1

There are 1 answers

0
Yoel On

try this

function filterJustT<T>(predicate: (i: any) => boolean): (arr: any[]) => T[] {
  return (arr: any[]) => arr.filter((i) => predicate(i)) as T[];
}

const filterJustStrings2 = filterJustT<string>(test);

TypeScript Playground