keyof typeof magic - Don't repeat yourself

keyof typeof allows you to easily create a type from the properites in an existing object

The keyof and typeof keyword are very useful keywords that you can use for many interesting things. One of which is creating types from the keys of an object.

The typeof keyword exists in the JavaScript world as well as the TypeScript world and it does similar things: get the type of a value.

The keyof keyword creates a type out of the keys of an object type. Let’s see examples:

type Person = {
  name: string;
  age: string;
  languages: string[]
}

let /**/key/**/: keyof Person
key: 'name' | 'age' | 'languages'

In this example, key would be a union type of the properties (keys) in the Person Type:

type Person = {
  name: string;
  age: string;
  languages: string[]
}

let key: keyof Person

key = 'name' // fine

/**/key/**/ = 'something'
Type '"something"' is not assignable to type 'keyof Person'

Let’s say we had an object like this:

const dutchTranslation = {
  "hello": "hoi",
  "good morning": "goedemorgen",
  "difficult": "moeilijk",
  "grandma": "oma"
}

The dutchTranslation object has key-value pairs where the key is the english word and the value is the dutch word. How can we create a key type from the keys in this object? You might think keyof:

const dutchTranslation = {
  "hello": "hoi",
  "good morning": "goedemorgen",
  "difficult": "moeilijk",
  "grandma": "oma"
}

type /**/TranslationKey/**/ = keyof dutchTranslation
'dutchTranslation' refers to a value, but is being used as a type here.

keyof does not work because it expects a type, not a value. In keyof dutchTranslation, dutchTranslation points to a value. So, what we have to do first is get the type of dutchTranslation. Here, we can use `typeof:

const dutchTranslation = {
  "hello": "hoi",
  "good morning": "goedemorgen",
  "difficult": "moeilijk",
  "grandma": "oma"
}

type DutchTranslationType = typeof dutchTranslation

DutchTranslationType will look like this:

{
  "hello": string;
  "good morning": string;
  "difficult": string;
  "grandma": string;
}

Now that we have the type, we can use keyof:

const dutchTranslation = {
  "hello": "hoi",
  "good morning": "goedemorgen",
  "difficult": "moeilijk",
  "grandma": "oma"
}

type DutchTranslationType = typeof dutchTranslation
type /**/TranslationKey/**/ = keyof DutchTranslationType
TranslationKey: 'hello' | 'good morning' | 'difficult' | 'grandma'

TranslationKey holds a union type of the keys in the object. You can also combine keyof and typeof like this:

type TranslationKey = keyof typeof dutchTranslation