12

I have two classes:

class Bar extends Foo { // Foo isn't relevant
  constructor(value) {
    if (!(value instanceof Foo)) throw "InvalidArgumentException: (...)";
    super();
    this.value = value;
  }
}

class Baz extends Bar {
  constructor(value) {
    super(value);
  }
}

The Bar constructor checks if value is an instance of Foo, it throws an error if it isn't. At least, that's what I wanted it to do. If you pass a Bar or a Baz as value, the if-statement returns true as well. The goal is to only let Foos through.
I found this answer already but that didn't really answer my question.

Sacha
  • 819
  • 9
  • 27
  • [This answer](https://stackoverflow.com/a/8715426/9524541) from the other quesiton you linked seems to work. Only a Foo object created with `new Foo()` would return "Foo" when calling `foo.constructor.name` – SystemGlitch Mar 21 '18 at 21:59
  • 1
    What do you think you need this for? It seems like you are misusing inheritance, as your goal is to break the [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). Don't do that. Every `Bar` and `Baz` instance should be valid where you want to have a `Foo` instance. – Bergi Mar 22 '18 at 02:30

7 Answers7

11

Check the constructor:

if (!value || value.constructor !== Foo)
  throw 'InvalidArgumentException: (...)';

or the prototype of the object (this is more similar to what instanceof does):

if (!value || Object.getPrototypeOf(value) !== Foo.prototype)
  throw 'InvalidArgumentException: (...)';
Mohammad Dehghan
  • 17,853
  • 3
  • 55
  • 72
2

You can use a comparison between Object.getPrototypeOf(yourObj) and Foo.prototype to see if yourObj is exactly an instance of Foo. And you can move up the chain by just continuing to call Object.getPrototypeOf for each level.

Example:

class Foo {}

class Bar extends Foo {}
class Baz extends Bar {}

const foo = new Foo();
const bar = new Bar();
const baz = new Baz();

// For this function:
// - level 0 is self
// - level 1 is parent
// - level 2 is grandparent
// and so on.
function getPrototypeAt(level, obj) {
    let proto = Object.getPrototypeOf(obj);
    while (level--) proto = Object.getPrototypeOf(proto);
    return proto;
}

console.log("bar is a foo:", bar instanceof Foo);
console.log("baz is a foo:", baz instanceof Foo);
console.log("foo is exactly a foo:", getPrototypeAt(0, foo) === Foo.prototype);
console.log("bar is exactly a foo:", getPrototypeAt(0, bar) === Foo.prototype);
console.log("bar is direct child of foo:", getPrototypeAt(1, bar) === Foo.prototype);
console.log("baz is direct child of foo:", getPrototypeAt(1, baz) === Foo.prototype);
console.log("baz is direct child of bar:", getPrototypeAt(1, baz) === Bar.prototype);
console.log("baz is grandchild of foo:", getPrototypeAt(2, baz) === Foo.prototype);
CRice
  • 29,968
  • 4
  • 57
  • 70
1

You should test if value's internal [[Prototype]] is exactly Foo.prototype. You can get the internal [[Prototype]] with Object.getPrototypeOf :

if ( Object.getPrototypeOf( value ) !== Foo.prototype )
   throw "InvalidArgumentException: (...)";
Paul
  • 139,544
  • 27
  • 275
  • 264
0

If you know all of your classes you can use

if(!(value instanceof Foo && !(value instanceof Bar) && !(value instanceof Baz)))
Sebastian Speitel
  • 7,166
  • 2
  • 19
  • 38
0

Here is the helper function:

function isDirectInstanceOf(object, constructor) {
    return Object.getPrototypeOf(object) === constructor.prototype;
}

console.log(
    isDirectInstanceOf([], Array), // true
    isDirectInstanceOf([], Object), // false
);
devope
  • 127
  • 11
  • This is correct, but essentially the same answer as already given by Mohammad, CRice and Paul – Bergi Apr 18 '23 at 19:00
  • I really appreciate your feedback. Actually, I think my answer is still valuable since it suggested the separate function and is pretty illustrative and well-focused. What do you think? – devope Apr 18 '23 at 19:19
  • If you want to focus on the helper function, I'd name it `isDirectInstanceOf` or (less accurate) `wasConstructedBy`. A "child" usually refers to a child class. – Bergi Apr 18 '23 at 19:28
-1

I coined the function for checking relationships between DOM Classes and elements

const getCreator = instance => Object.getPrototypeOf(instance).constructor.name;
// usage
getCreator(document.documentElement) === "HTMLHtmlElement;
daGo
  • 2,584
  • 26
  • 24
-2

The problem is that all of your classes you reference are descendants of Foo. Such that new Baz() instanceOf Bar && new Bar() instanceOf Foo === true. So when you ask is Bar instanceOf Foo, it will be true through inheritance.

Due to there being no Java getClass() equivalent in JS, you should use something like:

if (value.constructor.name !== Foo.name)
Joshua Manns
  • 525
  • 4
  • 6
  • What does `getClass()` in Java do that `value.constructor` doesn't in JS? – Bergi Mar 22 '18 at 02:32
  • 4
    You should never compare the names of classes. They might be mangled by minifiers, they might be optimised away, they might be overwritten or faked. Never rely on names. – Bergi Mar 22 '18 at 02:34
  • @bergi can you please check my answer – devope Apr 18 '23 at 18:19