3

I'm trying to create a generic class where a property type has to be either string or number (because it's used as an index).

If I try something like the code below it will complain that

TS2536: Type 'T' cannot be used to index type '{}'.

export class GenericClass<T> {
    public selectionMap: {[id: T]: boolean} = {};

    public isSelected(id: T): boolean {
      return !!this.selectionMap[id];
    }

    public toggleSelected(id: T): void {
        if (this.isSelected(id)) {
            delete this.selectionMap[id];
        } else {
            this.selectionMap[id] = true;
        }
        this.onChange();
    }
}

In my case, I always know T will be a number or string. Just not sure how to write it down to stay type-safe.

Andurit
  • 5,612
  • 14
  • 69
  • 121
  • Does this answer your question? [TypeScript - How to represent an index signature as a generic type](https://stackoverflow.com/questions/42155759/typescript-how-to-represent-an-index-signature-as-a-generic-type) – BizzyBob Dec 22 '22 at 14:39

2 Answers2

2

Why are you using generics in this case? Just use the types you want to have or define a type that fits your needs:

type Id = number | string;
export class GenericClass {
    public selectionMap: {[id: Id]: boolean} = {};

    public isSelected(id: Id): boolean {
      return !!this.selectionMap[id];
    }

    public toggleSelected(id: Id): void {
        if (this.isSelected(id)) {
            delete this.selectionMap[id];
        } else {
            this.selectionMap[id] = true;
        }
        this.onChange();
    }
}
Garuno
  • 1,935
  • 1
  • 9
  • 20
  • Nice solution, it didn't come to my mind. But I am afraid it has one minor problem. As this class is more like util. I will have to cast the results of every method called outside of this function. Because type wise function will always return number | string but I want to know that If I say at declaration it's used number it will return the number not the number | string. Or am I missing something and this can handle that? – Andurit Dec 22 '22 at 13:24
  • btw I give it a thumbs up for it already, it definitely is a solution. Just not sure if match my exact scenario. – Andurit Dec 22 '22 at 13:25
  • In that case the answer from @mh377 is more appropriate. Just with the code you posted it didn't seem neccessary to use a generic class. – Garuno Dec 22 '22 at 13:29
2

Try the following:

export class GenericClass<T = string | number> {}

or

export class GenericClass<T extends string, number> {}

Also, make sure the types you are using to call the class are lowercase string and number. See this blog for further information.

mh377
  • 1,656
  • 5
  • 22
  • 41
  • Thank you, that makes sense, I tried something similar but messed up syntax and it didn't work. – Andurit Dec 22 '22 at 13:20
  • I just tested this out and I'm still getting errors: `TS2536: Type 'T' cannot be used to index type '{}'.` – Andurit Dec 22 '22 at 13:36
  • I can't see the code you are using to call this class but are the objects primitives i.e. using lowercase 'string' and 'number'. See this blog for more details https://bobbyhadz.com/blog/typescript-type-cannot-be-used-as-an-index-type#:~:text=The%20error%20%22Type%20cannot%20be,or%20string%20when%20typing%20values. – mh377 Dec 22 '22 at 13:40