To handle a union type in a function where only one specific type is expected, you can utilize a type guard. This ensures that the function is called only when the true type of the argument matches what the function anticipates:
public getClassesNumbers(classes: ClassesAndSubjects[] | SchoolClass[]) {
if (classes && (classes[0] instanceof SchoolClass)) {
// Safe to call functionInside:
functionInside(classes as SchoolClass[]);
} else {
// ... do something else ...
}
}
This method works effectively when dealing with classes like ClassesAndSubjects
and SchoolClass
. However, for interfaces, using instanceof
isn't feasible. In such cases, a different type guard approach is necessary. For example, checking for a property unique to SchoolClass
:
public getClassesNumbers(classes: ClassesAndSubjects[] | SchoolClass[]) {
if (classes && classes[0] && ('someProp' in classes[0])) {
// Safe to call functionInside:
functionInside(classes as SchoolClass[]);
} else {
// ... do something else ...
}
}
A third alternative involves defining a more explicit type guard:
function isSchoolClassArray(x: any[]): x is SchoolClass[] {
return (x !== null && x.length > 0) ? ('someProp' in x[0]): false;
}
public getClassesNumbers(classes: ClassesAndSubjects[] | SchoolClass[]) {
if (isSchoolClassArray(classes)) {
// Safe to call functionInside:
functionInside(classes);
} else {
// ... do something else ...
}
}
The benefit of the last approach is that TypeScript effectively infers the type of classes
within the if
and else
blocks without requiring type assertions.
While these solutions may fail if the array is empty, it's not impactful since functionInside
will handle an empty array appropriately.