0

I would like my javascript data to be like this:

"dogs": {
    "ada": {
        age: 7,
        breed: poodle
    },
    "levin": {
        age: 5,
        breed: shitzu
    },
    ...
}

Where the name is an object key. I have this typescript class:

export class Dog {
  [dogName: string]: DogDetails;
}

The only way I could see that I could encapsulate methods to create accesssors is by adding these static methods to Dog:

  public static getDogName(dog: Dog): string {
    return Object.keys(dog).toString();
  }

  public static getDogDetails(dog: Dog): DogDetails {
    return dog[Dog.getDogName(dog)];
  }

Which is pretty horrible. One reason being is that static access is required:

Dog.getDogName(dog)  // this is how it must be done

And seems so contrary to how this should be written, which would be:

dog.getName();  // this unfortunately can't be done

The situation would be much nicer if there was a way to get access to the index signature value. However I could not find any way to achieve this. Is there a way or is there a better way to write this class?

Note that this is not possible with classes using index signatures:

public getName(): string {   // this won't work
    Object.keys(this).toString();
}

Since it complains Property 'getName' of type '() => string' is not assignable to string index type 'DogDetails'


Update after answer from @amiramw and comments conversation:

The angular component where this is being used is:

<ion-list *ngFor="let dog of dogs">
    <ion-item>
      <ion-avatar item-start>
        <img src="assets/img/dog.jpg"/>
      </ion-avatar>
      <h4>{{Dog.getName(dog)}}</h4>
      <p>{{Dog.getDogDetails(dog).breed}}</p>
    </ion-item>
  </ion-list>

After the feedback it seems I'll need to write:

<ion-list *ngFor="let dogName of getDogNames()">
    <ion-item>
      <ion-avatar item-start>
        <img src="assets/img/dog.jpg"/>
      </ion-avatar>
      <h4>{{dogName}}</h4>
      <p>{{getDogDetails(dogName).breed}}</p>
    </ion-item>
  </ion-list>

Where getDogNames() is:

  public getDogNames(): string[] {
    if (this.dogs) {
      return Object.keys(this.dogs);
    } else {
      return new Array<string>();
    }
  }

And getDogDetails() is:

  public getDogDetails(name: string): DogDetails {
    return this.dogs[name];
  }

Which works but isn't what I was looking for. I will give @amiramw credit for the suggestion as to a solution to my overall problem.

However I'd much appreciate knowing if there is a way to obtain the typescript index signature value??

HankCa
  • 9,129
  • 8
  • 62
  • 83

1 Answers1

1

You can create an interface for each dog's data and define a map from string to that interface. The interface can be named (for reuse) or anonymous:

enum Breed {
    poodle,
    shitzu
}
let dogs : { [key:string]: {age: number, breed: Breed} } = {
    "ada": {
        age: 7,
        breed: Breed.poodle
    },
    "levin": {
        age: 5,
        breed: Breed.shitzu
    }
};
amiramw
  • 506
  • 2
  • 10
  • How do you get the name? eg. "ada" – HankCa Jun 11 '17 at 07:38
  • You can get the dogs' list by calling `Object.keys(dogs)` and then you have the names and you can access each dog data by `doing dogs[dogName]`. Other option is to maintain the dogs list in an array and add `name` property to the interface. – amiramw Jun 11 '17 at 08:12
  • Ok I see your approach. I don't think your typescript code is essentially any different to what I have, it is just your approach of accessing the data. This may be how I have to do it, however it is contrary to how I think it should be done and different to text-book cases. I will update my question to qualify with the angular code being used to traverse this data. Thanks for your efforts. – HankCa Jun 11 '17 at 08:23