Is there a way to write a TypeScript typeguard against a complex combinatory type (that uses many "and" and "or" statements) to test the existence of one of the "or" types?
For example:
interface Type1 { cat: string }
interface Type2 { dog: boolean }
interface Type3 { mouse: number }
interface Type4 { elephant: string | number }
type CombinatoryType = (Type1 & (Type2 | Type3)) | Type4;
If we have a typeguard to check the existence of Type2 and Type4 (called hasType2
and hasType4
respectively), calling them in the following order should produce the following results:
function doSomething(data: CombinatoryType) {
if (hasType2(data)) {
// 'data' is now of type: (Type1 & Type2) | Type4
if (hasType4(data)) {
// 'data' is now of type: Type1 & Type2 & Type4
}
}
}
And calling them in the opposite order should produce the following results:
function doSomething(data: CombinatoryType) {
if (hasType4(data)) {
// 'data' is now of type: (Type1 & (Type2 | Type3)) & Type4
if (hasType2(data)) {
// 'data' is now of type: Type1 & Type2 & Type4
}
}
}
Important to note:
- As demonstrated above, the typeguard should not lose previous type information (e.g. from previous type assertions made)
- The typeguard should also not use an explicit return type (e.g.
data is (Type1 & Type2) | Type4
) as the combinatory type could get very long. This also tricks VSCode's type inference into thinking the asserted value is exactly that type (although intellisense still works fine).
If my understanding is correct in your provide code, you want to create type guards for these
Type2
andType4
in theCombinatoryType
type. Isn't it?If so, here is an example I can provide:
In the above sample, we're using the
in
operator to check if thedog
property exists indata
forhasType2
, and if theelephant
property exists indata
forhasType4
.The
in
operator is a type guard that checks if a property exists in an object.The data is (Type1 & Type2) | Type4 and data is (Type1 & (Type2 | Type3)) & Type4 syntax is used to tell TypeScript that if the function returns
true
, thendata
is of the specified type.Now, you can use these type guards in your
doSomething
function:In this function, if
hasType2(data)
returnstrue
, then thatdata
is of type (Type1 & Type2) | Type4 within the firstif
. IfhasType4(data)
also returnstrue
, then thatdata
is of type Type1 & Type2 & Type4 within the secondif
.The same logic applies to the second
doSomething
function, where the type guards are called in the opposite order.Edit
I will provide this sample;
In the sample above,
hasType2
andhasType4
functions, they check whetherdata
has properties ofType2
andType4
respectively, and if so, they narrow down the type ofdata
toCombinatoryType & Type2
andCombinatoryType & Type2 & Type4
respectively. This allows you to preserve the previous type information ofdata
and add new types to it.