I have two suggestions. Neither is perfectly typesafe, but the lack of safety is contained within small functions which will be easy to test and manually verify. With both, there is the risk that if you pass in an unrelated object with the node.ownerDocument.defaultView.Element
property you will get incorrect results, but that seems like a very small risk. I haven't tested these at runtime, but any required tweaks should be relatively minor.
declare class Element {};
function asElement(node: any): ?Element {
const OwnElement = node?.ownerDocument?.defaultView?.Element;
if (OwnElement != null && node instanceof OwnElement) {
return node;
} else {
return null;
}
}
function doThingWithElement(whatever: mixed): void {
const element = asElement(whatever);
if (element != null) {
(element: Element);
// Expected error
(element: string);
}
}
(try)
In this example, asElement
is a function which does the instanceof check with the iframes issue in mind, and returns the argument if and only if it passes the check. Then, you can refine using an ordinary nullness check.
declare class Element {};
declare function isElement(node: any): boolean %checks (node instanceof Element);
function isElement(node) {
const OwnElement = node?.ownerDocument?.defaultView?.Element;
return OwnElement != null && node instanceof OwnElement;
}
function doThingWithElement(whatever: mixed): void {
if (isElement(whatever)) {
(whatever: Element);
// Expected error
(whatever: string);
}
}
(try)
This creates an isElement
function which you can use directly to perform the refinement.
As an aside, Element
is the type if an instance of the Element
class, not the class itself. Class<Element>
is the type of the class itself, but it appears that Flow doesn't perform type refinements where the right hand side has a dynamically-created class.