1

I have two classes and a service storing data in a map with a string key to access, stored data is of type of one of these 2 classes.

class DummyObj {
  constructor(public text: string, public dummy: string){}
}

class DummySecondObj {
  constructor(public text: string, public dummySecond: number){}
}

type ValidTypes = DummyObj | DummySecondObj | undefined;

class ServiceClass {

  private readonly storage = new Map<string, ValidTypes>();

  constructor() {
    this.storage.set('key1', { text: 'dummy 1', dummy: 'dummy element 1' });
    this.storage.set('key2', { text: 'dummy 2', dummySecond: 4 });
    
    this.storage.set('key3', new DummyObj('dummy 1','dummy element 1'));
    this.storage.set('key4', new DummySecondObj('dummy 1', 4));
  }

  getData(resourceKey: string): ValidTypes {

    if (this.storage.has(resourceKey)) {
      const meta = this.storage.get(resourceKey);
      //console.log(meta);

      if (meta instanceof DummyObj) {
        console.log('is DummyObj');
        return (this.storage.get(resourceKey) as DummyObj);
      }
      else if (meta instanceof DummySecondObj) {
        console.log('is DummySecondObj');
        return (this.storage.get(resourceKey) as DummySecondObj);
      }
      else {
        console.log('not found')
        return undefined
      }
    }

    return undefined;
  }
}

When called the method getData I would expect that instanceof is able to see the structure equal to my class and return with the correct state but this is not true if not created with the constructor but just as inline object. There is a way to make it work also with the inline generic object initialization? And can someone point me out why exactly is it not working? thank you


const service = new ServiceClass();
var x = service.getData('key1');
var x = service.getData('key2');
var x = service.getData('key3');
var x = service.getData('key4');

Here also the running example live example


[Updated]

Second example

2
  • The problem is that typescript doesnt really do typechecking in runtime. For more info learning-notes.mistermicheels.com/javascript/typescript/… Commented Mar 10, 2022 at 15:41
  • Not true @Yftach, as classes are also available at runtime. Commented Mar 10, 2022 at 15:48

1 Answer 1

2

It is not working because your object simply is not an instance of any of the classes you defined. Your right that they happen to have the same properties, but it isn't of the same type.

Consider two classes

class Person {
  constructor(public name: string) {}
}

class Animal{
  constructor(public name: string) {}
}

both classes have a property called name. However, an instance of one class can never be an instance of the other.

Imagine this would work:

new Animal("simba") instanceof Person // imagine this is true

That would be absurd.

In the same way, creating an object inline does not make it an instance of any other class you defined, just because the properties are the same. Inline created objects are an instance of Object.

What you want is to convert the meta data from the storage to an actual instance of your class. There is no default way to do this in javascript. But a simple example could be:

class DummyObj {
  constructor(public text: string, public dummy: string){}

  static isValidMetadata(data: any) {
    return data.hasOwnProperty('text') && data.hasOwnProperty('dummy');
  }
}

class DummySecondObj {
  constructor(public text: string, public dummySecond: number){}

  static isValidMetadata(data: any) {
    return data.hasOwnProperty('text') && data.hasOwnProperty('dummySecond');
  }
}

class ServiceClass {

  private readonly storage = new Map<string, ValidTypes>();

  constructor() {
    this.storage.set('key1', { text: 'dummy 1', dummy: 'dummy element 1' });
    this.storage.set('key2', { text: 'dummy 2', dummySecond: 4 });
    
    this.storage.set('key3', new DummyObj('dummy 1','dummy element 1'));
    this.storage.set('key4', new DummySecondObj('dummy 1', 4));
  }

  getData(resourceKey: string): ValidTypes {

    if (this.storage.has(resourceKey)) {
      const meta = this.storage.get(resourceKey);
      //console.log(meta);

      if (DummyObj.isValidMetadata(meta)) {
        console.log('is DummyObj');
        return (new DummyObj(meta.text, meta.dummy));
      }
      else if (DummySecondObj.isValidMetadata(meta)) {
        console.log('is DummySecondObj');
        return (new DummySecondObj(meta.text, meta.dummySecond));
      }
      else {
        console.log('not found')
        return undefined
      }
    }

    return undefined;
  }
}

Notice the code does two things:

  • validate that the data from storage has the correct properties
  • Convert the meta data to an actual instance of the class
Sign up to request clarification or add additional context in comments.

4 Comments

you example is clear and of course make perfect sense, but on objection. here a link to a sub version of the same example. What if also the stored data are loaded from outside, I can still load object not instance of class. How can I protect in order to be aware at compile time that only instance of those class are loaded. I just update code with a second example
I updated my answer to contain an example on how to approach your problem.
Thank you. It is a good approach also if I think I will go with something different, a property typeDiscriminant: 'Animal' or typeDiscriminant: 'Person' initialized with some different types in each class (to be extended much more easily in the future) in order to perform a simple switch case. But never the less this reply to my initial question and understand why. @yadejo (vote up for the question will be appreciated if is well formed)
If you can add a discriminator property, that is definitely an even better solution :) best of luck!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.