18

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.

5 Answers 5

7

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.

Sign up to request clarification or add additional context in comments.

3 Comments

why do you recommend using enum, when it will generate redundant code
What particular redundant code will it generate? IIRC it will generate the same const KeyboardEventKeys variable, there is no redundancy there
enum d {A} --> cons d= {0:A,A:0} while i would do d = {A:'A'}
5

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

Comments

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);

3 Comments

This doesn't allow me to pass the raw string doSomething("Enter");
Also, I don't like enums because they generate more redundant code.
Why do you have to pass "enter as raw string? You can pass KeyboardEventKeys.Enter instead?
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

1 Comment

This works, although it is locked to one single value type. By using Entries from this answer you will be able to simply use const entries = Object.entries(myVar) as Entries<typeof myVar>; without having to explicitly specifying the object type and it might have members of different types.
0

Have you tried this:

keyof { [key: string]: KeyboarEventKeys }

1 Comment

It works, but you won't get the auto-completion whose inclusion is implied by wanting to use keyof

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.