3

Is there a way to check if something is an instance of an object without importing that object?

The class that I am using instanceof is a child of the current class. I have a method that runs a function and returns a new instance of either Model or Row based on some if statements. Model extends the class that has the method that I am using instanceof in, and I want to check if it is an instance of Model and then do some additional operations.

export class Base {
    public firstOrFail() {
        let val = this.first() // Returns a new instance of Model or Row
        if(val instanceof Model && val.length == 0) {
            throw new Error('No values')
        }
    }
}

Then I have two files each looking like this:

// File Model.ts
import { Base } from './Base'
export class Model extends Base {}

// File Row.ts
export class Row {}

The issue I am having is if I import Model inside base I get a circular dependency loop. If I don't require it, I get an error:

Cannot find name 'Model'

So is there a way I can do instanceof within the Base class?

There are two ways this can be used:

new Base().firstOrFail()

or

new Model().firstOrFail()
Get Off My Lawn
  • 34,175
  • 38
  • 176
  • 338
  • import Model . its right there in file model – JoshKisb Jan 06 '18 at 02:06
  • What's the purpose of using `instanceof Model` there? Why do you need it? – JLRishe Jan 06 '18 at 02:48
  • Because `Base` also needs access to the `firstOrFail` method. First will return an instance of `Row | Model`, if the length of the internal array of the `Model` is zero, it should throw an error a.k.a. "Fail". However row, doesn't have an internal array, so it doesn't need to be checked there – Get Off My Lawn Jan 06 '18 at 03:03
  • currently I am using `if ('length' in val && val.length == 0)` this works in JavaScript, but typescript complains about it. `Property 'length' does not exist on type 'this | Row'` (this being Model) this is a bug btw: https://github.com/Microsoft/TypeScript/issues/20893 – Get Off My Lawn Jan 06 '18 at 03:10
  • So, it's generally considered bad form for a base class to need to know anything about a derived class. The better answer here would be to fix your logic and structure so that doesn't need to happen at all. – jfriend00 Jan 06 '18 at 03:40

2 Answers2

4

So is there a way I can do instanceof within the Base class?

Not easily unless you put them in the same file. instanceof requires the object you're testing for to be defined before you can use it. There are some hacks you could use that would allow you to keep them in separate files because you don't actually need the reference to the Model class when the Base object is defined (it's only needed when a Base object is instantiated), but I would recommend fixing the design rather than pursuing this.

It's generally considered bad OO form for a base class to need to know anything about a derived class. Any behavior that is specific to the derived class should be implemented in the derived class and the base class can always faciliate letting the derived classes do what they need to do with an appropriate design. In 20 years of OO design, I've never found a legit reason for a base class to have to know anything about a derived class.

So, the better answer here would be to fix your logic and structure so that your Base class does not need to know anything about the Model class.

One implementation you could use that would accomplish this would be to override firstOrFail() in the Model class. You can convert this to typescript, but this is how it would look like in plain ol' ES6:

// Base.js
export class Base {
    firstOrFail() {
        // common, shared implementation
        // derived classes may override to provide validation
        return this.first();
    }
}

// Model.js
import { Base } from './Base'

class Model extends Base {
    constructor() {
       super();
    }
    firstOrFail() {
        let val = super.firstOrFail();
        if (val.length == 0) {
            throw new Error('No values')
        }
        return val;
    }
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Perfect! I just realized I was already doing part of that :), so now I just need to move that part of the logic to the Model – Get Off My Lawn Jan 07 '18 at 20:18
  • What sort of pattern would you use if you wanted a method in the parent class that would create an array of different types of derived class instances? I'm sure I am doing something wrong but I can't figure out how to approach this without creating circular dependencies. – joshhunt Sep 07 '18 at 04:50
  • @joshhunt - We'd have to understand more about what you're actually trying to accomplish. I'd suggest you write your own question, describe the actual design problem and why you're trying to end up with this array in a base class and then we can perhaps suggest a better design. The short answer is don't do that which is why we need to understand the actual design problem you're trying to solve so we could make a from scratch suggestion. – jfriend00 Sep 07 '18 at 06:14
  • @joshhunt - If you do post another question, you can drop a link here in a comment and I'll take a look at it. – jfriend00 Sep 07 '18 at 06:34
  • @jfriend00 https://stackoverflow.com/questions/52248742/javascript-how-to-reuse-method-for-creating-child-instances-without-creating-ci Hopefully this make sense, thanks! – joshhunt Sep 09 '18 at 21:21
0

The short answer is "no" -- you cannot use instanceof with an undefined operand.

However, that doesn't necessarily mean you can't discover whether an object is an instance of the class.

instanceof looks at the prototype chain. You can do the same thing manually. That may then require you to use some duck-typing to determine whether the prototype walks and quacks like a Model.

Tom
  • 8,509
  • 7
  • 49
  • 78