I've followed the example explained here to create an Or
function which takes a number of type guards and returns a type guard for the union type. e.g.: x is A + x is B => x is A | B
. However I'm not able to use the returned function as an argument to an Array.filter
.
Definitions:
type TypeGuard<A, B extends A> = (a: A) => a is B;
type GuardType<T> = T extends (o: any) => o is infer U ? U : never
class A { q: any }
class B { p: any }
declare function isA(x: any): x is A
declare function isB(x: any): x is B
function Or<T extends TypeGuard<any, any>>(guards: T[]): T extends TypeGuard<infer A, any> ? (a: A) => a is GuardType<T> : never;
function Or<T extends TypeGuard<any, any>>(guards: T[]) {
return function (arg: T) {
return guards.some(function (predicate) {
predicate(arg);
});
}
}
Code example:
let isAOrB = Or([isA, isB]); // inferred as ((a: any) => a is A) | ((a: any) => a is B)
let isOr_value = isAOrB({ q: 'a' }); // here isAOrB is inferred as (a: any) => a is A | B which is what I want
[{}].filter(isAOrB).forEach(x => { }); // here I expected x's type to be inferred as A | B because of the type guard, however filter's overload is the regular one, returning the same {}[] type as the source array
I know I can explicitly write a lambda expression as the filter
argument to force the type inference:
[{}].filter((x): x is A | B => isAOrB(x)).forEach(x => { });
But this is exactly what I'd like to avoid.
The same problem with an And function combining type guards
I've used the UnionToIntersection
construct shown here but cannot correctly type the And
function, here's my attempt and the error I'm getting:
function And<T extends TypeGuard<any, any>>(guards: T[]): T extends TypeGuard<infer A, any> ? (a: A) => a is UnionToIntersection<GuardType<T>> : never;
// the above gives error A type predicate's type must be assignable to its parameter's type.
Type 'UnionToIntersection<GuardType<T>>' is not assignable to type 'A'.
Type 'unknown' is not assignable to type 'A'.