class Model {
static isModel(object: any): object is Model { // `object` is an instance of `Model` class, if...
return !!object && // It is truthy AND...
!!object.__proto__.constructor.isModel && // It contains a static property with the same name as this function AND...
typeof object.__proto__.constructor.isModel === "function"; // That static property is a function.
}
}
This class contains a static type narrowing function which will check if the object passed to it is of the same type as this class by checking if the object passed to it contains the very static function by which this check is executed.
While this isn't completely fool-proof, it is sufficient to be highly confident that the type narrowing is correct.
The only way you could get a false positive is if the object you passed to the function ALSO happened to contain a static function with the same name. This suggests the more unique you can make your function name, the more likely your type narrowing is correct.
Model.isModel(null); // false
Model.isModel(new Set()); //false
Model.isModel(); //false
Model.isModel({foo: "bar"}); // false
Model.isModel(new Model()); // true
Some assumptions were made based on observation, but may be wrong in some edge cases.
- Truthy values always contain a
__proto__
property - The
__proto__
property always contains aconstructor
property. - Static properties exist directly on the
constructor
property. - Non-static properties DO NOT exist on the
constructor
property, they exist either on the instance itself (for non-function properties) or on the__proto__
property of the instance (for function properties).