15

I have the following code:

const KeyboardEventKeys = {
  Escape: 'Escape',
  Enter: 'Enter',
  Tab: 'Tab'
};

type KeyboardEventKeys = keyof (typeof KeyboardEventKeys);

function doSomething(key: KeyboardEventKeys) {}

When I'm passing to a function the value of one of the object properties it yells at me:

doSomething(KeyboardEventKeys.Enter);

One solution is to cast as KeyboardEventKeys, but it's a redundant solution. How can I do it without it?

I also don't want to add doSomething(key: KeyboardEventKeys | string) because I will lose the type guard.

undefined
  • 6,366
  • 12
  • 46
  • 90

5 Answers5

6

The solution to use an enum is a good one, and I would recommend you use it.

The reason you get an error is that typescript will not infer string literal types for const members. You can force the compiler to infer string literal types by using an extra function when you create the const:

function createEnum<T extends { [P in keyof T]: P }>(o: T) {
    return o
}
const KeyboardEventKeys = createEnum({ // typed as { Escape: "Escape"; Enter: "Enter"; Tab: "Tab"; }

    Escape: 'Escape',
    Enter: 'Enter',
    Tab: 'Tab'
});

type KeyboardEventKeys = keyof (typeof KeyboardEventKeys);

function doSomething(key: KeyboardEventKeys) { }
doSomething("Enter")
doSomething("") //err
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
6

Since TypeScript 3.4 you may force the compiler to preserve literal types by using special as const cast construction. So here you may write

const KeyboardEventKeys = {
  Escape: 'Escape',
  Enter: 'Enter',
  Tab: 'Tab'
} as const;

type KeyboardEventKeys = keyof typeof KeyboardEventKeys;

function doSomething(key: KeyboardEventKeys) {}

But, ultimately I recommend using [const] enum KeyboardEventKeys { ... } instead.

Veetaha
  • 833
  • 1
  • 8
  • 19
2

Solution is to use enums instead of objects.

enum KeyboardEventKeys {
  Escape ='Escape',
  Enter = 'Enter',
  Tab = 'Tab'
};


function doSomething(key: KeyboardEventKeys) {}

You can now pass variables like so

doSomething(KeyboardEventKeys.Enter);
Praveen
  • 2,400
  • 3
  • 23
  • 30
2

I am posting this here as it matches the thread title.

If you have an object of a Key value pair. You want to loop it with a forEach pulling off the key as a keyof YourObject and the value as a string. (example being input field name and value list) Here is what you need to do.

type FormDataArray = [ keyof FormStateObjectType,  string ];

const entries = Object.entries(state.formData) as FormDataArray[];
entries.forEach(([fieldName, fieldValue]) => {
    // Process code here
});

Results in:

  • fieldName being of type keyof FormStateObjectType
  • fieldValue being of type string
hlovdal
  • 26,565
  • 10
  • 94
  • 165
Centerwork
  • 139
  • 2
  • 4
  • This works, although it is locked to one single value type. By using `Entries` from [this answer](https://stackoverflow.com/a/60142095/23118) you will be able to simply use `const entries = Object.entries(myVar) as Entries;` without having to explicitly specifying the object type and it might have members of different types. – hlovdal Jan 11 '23 at 13:38
1

Have you tried this:

keyof { [key: string]: KeyboarEventKeys }
David Kabii
  • 642
  • 6
  • 17