Sturm
Sturm

Reputation: 759

TypeScript enum with objects

I am attempting to use Javarome's answer to a previous TypeScript question regarding how to use an object in an enum:

class Material {
  public static readonly ACRYLIC = new Material(`ACRYLIC`, `AC`, `Acrylic`);
  public static readonly ALUM = new Material(`ALUM`, `AL`, `Aluminum`);
  public static readonly CORK = new Material(`CORK`, `CO`, `Cork`);
  public static readonly FOAM = new Material(`FOAM`, `FO`, `Foam`);

  // private to diallow creating other instances of this type.
  private constructor(
    public readonly key: string,
    public readonly id: string,
    public readonly name: string
  ) {}

  public toString(): string {
    return this.key;
  }
}

Unfortunately, I am running into an issue later in the code when I attempt to use bracket-syntax (because it's in a for-of loop):

const materials: string[] = [`ACRYLIC`, `FOAM`];
for (const materialKey of materialsArray) {
  const material: Material = Material[materialKey];
  // ...
}

That pops up a huge TS error [TS(7053)] with the following message:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof Material'.

No index signature with a parameter of type 'string' was found on type 'typeof Material'.ts(7053)

I have been Googling for hours but have found nothing that helps. Is there any way to reference this "enum" using bracket-syntax?

Upvotes: 3

Views: 5618

Answers (1)

Maciej Sikora
Maciej Sikora

Reputation: 20152

The issue with this code is exactly:

const materials: string[] = [`ACRYLIC`, `FOAM`];

There is no relation between possible properties of Material static class and a list of strings. The key to the problem is to specify in type that the list which we have is a list of only allowed properties which have values as Material type.

It can be achieved by Exclude type utility. Take a look at below example:

type MaterialKeys = Exclude<keyof typeof Material, 'prototype'>;
const materialsArray: MaterialKeys[] = [`ACRYLIC`, `FOAM`];
for (const materialKey of materialsArray) {
  const material: Material = Material[materialKey];
  // ...
}

For more informations: Exclude<keyof typeof Material, 'prototype'>; will take all keys of Material type and exclude from it the prototype, so we will in result have all static fields, and that is what we want though.

Upvotes: 2

Related Questions