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
Type2andType4in theCombinatoryTypetype. Isn't it?If so, here is an example I can provide:
In the above sample, we're using the
inoperator to check if thedogproperty exists indataforhasType2, and if theelephantproperty exists indataforhasType4.The
inoperator 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, thendatais of the specified type.Now, you can use these type guards in your
doSomethingfunction:In this function, if
hasType2(data)returnstrue, then thatdatais of type (Type1 & Type2) | Type4 within the firstif. IfhasType4(data)also returnstrue, then thatdatais of type Type1 & Type2 & Type4 within the secondif.The same logic applies to the second
doSomethingfunction, where the type guards are called in the opposite order.Edit
I will provide this sample;
In the sample above,
hasType2andhasType4functions, they check whetherdatahas properties ofType2andType4respectively, and if so, they narrow down the type ofdatatoCombinatoryType & Type2andCombinatoryType & Type2 & Type4respectively. This allows you to preserve the previous type information ofdataand add new types to it.