0

Introduction

I have an enum and want to map it's values to more descriptive strings. I also want the compiler to recognize when I missed a mapping. So I did something like this:

enum Foo { A, B }

const fooDescriptions: {[key in Foo]: string} = {
    [Foo.A]: 'Option A',
    [Foo.B]: 'Option B',
}

This works as intended. Now I'd like to declare a more expressive, generic type to replace {[key in Foo]: string} (Not that it would be that hard to understand, just trying to make things shiny).

My ultimate goal ist a generic type like this

type EnumDescriptionType<T> = /* ??? */;
const fooDescriptions: EnumDescriptionType<Foo> = { /* keys of Foo */ };

All the following code can also be reproduced on TS playground.

Attempt 1

type EnumDescriptionType1<T> = { [key in T]: string };

yields error

Type 'T' is not assignable to type 'string | number | symbol'.
  Type 'T' is not assignable to type 'symbol'.

I don't really understand why. I'm used to C++-templates and don't yet fully comprehend the generic type system of typescript.

Attempt 2

type EnumDescriptionType2<T> = { [key in keyof T]: string };

const localFooDescriptions2: EnumDescriptionType2<Foo> = fooDescriptions;

yields error

Type '{ 0: string; 1: string; }' is not assignable to type 'EnumDescriptionType2<Foo>'.
  Type '{ 0: string; 1: string; }' is not assignable to type 'Foo.B'.

At this point I was honestly just trying things without understanding what I did. I would be thankful for clarification.

Actual question

I've also read this question, but they don't use templates. Soooo ...

... How can I declare a type like {[key in Foo]: string}, where Foo is a generic parameter that can be replaced with any enum?

Lukas-T
  • 11,133
  • 3
  • 20
  • 30

1 Answers1

1

You need to make sure T can be a key, so in the first attempt you can solve the issue by changing it to:

type EnumDescriptionType1<T extends keyof any> = { [key in T]: string };

Also, a suggestion, you can use the built in Record type in this case:

type EnumDescriptionType1<T extends keyof any> = Record<T, string>;

You can replace the keyof any with an explicit string | symbol | number, but IMO keyof any gives a more readable code in this case.

YohaiM
  • 383
  • 2
  • 9
  • In my eyes that's magic, but it works :D Thanks for the quick reply, I will accept it as soon as the system allows it. – Lukas-T Apr 09 '21 at 07:58