Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infer union types of type guards in TypeScript

Tags:

typescript

TypeScript seems to have problems to infer union types of type guards. As an example, consider a function to combine an array of type guards with the following signature

function combine<T>(guards: ((x: any) => x is T)[]): (x: any) => x is T

and consider the following type guards with A and B having different properties

function isA(x: any): x is A
function isB(x: any): x is B

Now I would expect combine([isA, isB]) to work and have the inferred type (x: any) => x is A | B but instead I get an error saying that an argument of type((x: any) => x is A | (x: any) => x is B)[] is not assignable to parameter of type (x: any) => x is A, meaning that T is inferred as A rather than A | B.

When specifying T explicitely, i.e. combine<A|B>([isA, isB]), it works as expected. Is there a way to change the signature of combine such that this could be inferred?

like image 346
ipsec Avatar asked Mar 06 '23 11:03

ipsec


1 Answers

You can use a type parameter to denote the whole function instead of just the guarded type. This allows the compiler to infer a union of guard functions. We can then use a conditional type to extract the union of guarded types :

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

declare function combine<T extends ((x: any) => x is any)>(guards: T[]): (x: any) => x is GuardType<T>

let isAB = combine([isA, isB]); // (x:any) => x is A|B
like image 57
Titian Cernicova-Dragomir Avatar answered Mar 31 '23 00:03

Titian Cernicova-Dragomir