Using keyof and typeof for types efficiently in TypeScript
Tags: javascript, typescript, webdev, beginners
Published: 2023-02-15 06:14:44

---
title: Using keyof and typeof for types efficiently in TypeScript
published: true
description: Let's learn to generate complex types with keyof and typeof as an efficient way of coding.
tags: ["javascript", "typescript", "webdev", "beginners"]
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6myxf0nv05455qjakd7g.png
series: Efficient TypeScript
canonical_url: https://mayashavin.com/articles/types-from-constants-typescript
---

In this article, we will learn how to extract and declare constant types by combining the type operators `typeof`, `keyof` and enums to benefit your codebase.

## Prerequisites

For a better coding experience, you should install TypeScript in your IDE, such as [VSCode](https://code.visualstudio.com/docs/languages/typescript). It will provide you with a lot of essential features such as error highlighting, IntelliSense, linting, etc... You should also install some extensions such as [JavaScript and TypeScript Nightly](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-next), [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), etc.

## Understanding `typeof`

In TypeScript, we can use `typeof` to extract the type of a variable or a property, as shown in the below example:

```typescript
const Name = {
    firstName: 'Maya',
    lastName: 'Shavin'
};

let myName: typeof Name;
```

In the above code, we declare the `myName` variable with the type extracted from the variable `Name`, which will be an object with two properties - `firstName` and `lastName`. When hovering on `myName` in your IDE, TypeScript will show you the type for `myName` concluded from `Name` as in the following screenshot:

<img src="https://res.cloudinary.com/mayashavin/image/upload/f_auto,q_auto/v1676408278/articles/typescript/Name_type" loading="lazy" alt="A screenshot displaying the type Name with first name and last name as its properties" class="mx-auto"/>

Note here with `typeof`, if the variable to extract type is an Object, the type received will be a complete type structure, and each property has its type (such as type of `myName` will have two fields - `firstName` of type `string`, and `lastName` of kind `string`). 

Another valuable scenario for `typeof` is combining it with `ReturnType` to extract the type of the returned data from a function. To do so, we perform the following:

1. Get the function type of the declared function using `typeof`
2. Use `ReturnType<T>` with `T` is the extracted type in step 1 to get the type of the return value from `T`.

The following example demonstrates how we can extract the return type of `decoupleName()`

```typescript
function decoupleName (name: string) {
    const [firstName, ...remaining] = name.split(" ");

    return {
        firstName,
        lastName: remaining.reduce((text, word) => text ? `${text} ${word}` : word, '')
    }
}

type NameObj = ReturnType<typeof decoupleName>
```

TypeScript will automatically refer to the correct type of `NameObj`, shown in the screenshot below:

<img src="https://res.cloudinary.com/mayashavin/image/upload/f_auto,q_auto/v1676408278/articles/typescript/NameObj" loading="lazy" alt="A screenshot displaying the type NameObj with first name and last name as its properties" class="mx-auto"/>

Additionally, we can use `typeof` in TypeScript to act as the type guard within a conditional block, just like in JavaScript, though in this case, it will mainly work for primitive types like `string`, `object`, `function`, etc...

Now we understand `typeof` and its usage. Next, we will explore `keyof`.

## Understanding `keyof`

While `typeof` produces the type represented by a variable, `keyof` takes an object type and produces a type that is the liberal union of that variable's keys, as seen below:

```typescript
interface User {
    firstName: string;
    lastName: string;
};

type UserKeys = keyof User
```

The above equals the following declaration:

```typescript
type UserKeys = "firstName" | "lastName"
```

However, unlike `typeof`, you can't use `keyof` directly on a variable. TypeScript will throw an error for the following code:

```typescript
const Name = {
    firstName: 'Maya',
    lastName: 'Shavin'
};

type NameKeys = keyof Name; //error
```

To extract types from object variables, such as an object map of constant values, we need to combine `keyof` and `typeof`, which we will learn next.

## Extract type from an object's keys (or properties)

Take the following `ImageVariables`, which acts as a map for variables used in modifying an image, for instance:

```javascript
export const ImageVariables = {
  width: 'w',
  height: 'h',
  aspectRatio: 'ar',
  rotate: 'a',
  opacity: 'o',
} as const;
```

Note here we need the `const` at the end of the object to indicate it as *read-only*. Otherwise, TypeScript won't allow us to extract the types from it due to the risk of modifying the object's internal properties on the go.

`ImageVariables` contains the mapping from its keys to matching variable symbols used in transforming an image according to the [Cloudinary mechanism](https://cloudinary-build-url.netlify.app/). To generate a type based on the `ImageVariables`'s properties (or keys), we perform the following:

1. Get the type representing `ImageVariables` using `typeof`
   
   ```typescript
   type ImageVariablesType = typeof ImageVariables
   ```

2. Extract a new type based on the keys of the kind `ImageVariablesType`, using `keyof`
   
   ```typescript
   type ImageFields = keyof ImageVariablesType
   ```

Alternatively, we can combine the two steps above into one, as follows:

```typescript
type ImageFields = keyof typeof ImageVariables
```

That's it. We now have `ImageFields` type, which contains the accepted fields for `ImageVariables`, like in the screenshot below:

<img src="https://res.cloudinary.com/mayashavin/image/upload/f_auto,q_auto/v1676408278/articles/typescript/ImageFields" loading="lazy" alt="A screenshot displaying the type ImageFields with all the keys of ImageVariables as its acceptable values" class="mx-auto"/>

We can now use this generated type as follows:

```typescript
const transformImage = (field: ImageFields) => {
    const variable = ImageVariables[field]

    //do something with it
}
```

By declaring the type based on properties of `ImageVariables`, the flow for any usage of `transformImage` is secure, and we can ensure that the `field` passed will always need to exist within `ImageVariables`. Otherwise, TypeScript will detect and alert the user of any error. 

<img src="https://res.cloudinary.com/mayashavin/image/upload/f_auto,q_auto/v1676408278/articles/typescript/error_gravity" loading="lazy" alt="A screenshot displaying error by TypeScript when passing a non-exist value in ImageFields to the function transformImage" class="mx-auto"/>

Also, Intellisense will inform the user of the acceptable values, limiting the possibility of passing the wrong value. 

<img src="https://res.cloudinary.com/mayashavin/image/upload/f_auto,q_auto/v1676408278/articles/typescript/hint_intellisense" loading="lazy" alt="A screenshot displaying a hint dropdown for accepted values to pass to transformImage" class="mx-auto"/>


On a side note, the type-check behaves similarly to using the `hasOwnProperty()` check in runtime, though it only happens in compile time.

Sound straightforward enough. What if we want to extract the values of the keys from `ImageVariables` into a new type? Let's look at it next.

## Extract type from values of object's keys (or properties)

If we want to generate a type from the values of the keys of `ImageVariables`, we can do the following:

```typescript
type VariableValues = typeof ImageVariables[ImageFields]
```

Since we already declare `ImageVariablesType` as type of `ImageVariables`, we can rewrite the above as:

```typescript
type VariableValues = ImageVariablesType[ImageFields]
```

With the above code, we now have a new type, `VariableValues` which accepts the following values:

<img src="https://res.cloudinary.com/mayashavin/image/upload/f_auto,q_auto/v1676408278/articles/typescript/variable_extract" loading="lazy" alt="A screenshot displaying the type generated from values of ImageVariables' keys" class="mx-auto"/>

Generating type from values and keys of a named constant object is advantageous in many scenarios, such as when you have to work with various data mappings, and standard keys or values are mapping between them. Using `keyof` and `typeof` on the object mapping can help create the connection between related maps and types, thus avoiding potential bugs.

Alternatively, we can combine enums and `typeof` to achieve the same goal.

## Bonus: working with enums

Enums are a convenient and organized way to declare named constant types. It allows us to create a set of distinct constant values, and each enum field is either numeric or string-based. We can rewrite our `ImageVariables` as follows:

```typescript
enum EImageVariables {
  width = 'w',
  height = 'h',
  aspectRatio = 'ar',
  rotate = 'a',
  opacity = 'o',
}
```

One advantage of using an enum is we can use the name of the enum as the type for accepted values declaration. Hence, instead of the following code:

```typescript
type VariableValues = typeof ImageVariables[ImageFields]

function transform(value: VariableValues) {
    //perform something
}
```

We can rewrite using `EImageVariables` as follows:

```typescript
function transform(value: EImageVariables) {
    //perform something
}
```

The type-check performs the same for less code 😉. Nevertheless, for getting the type from the keys (or properties) of the declared enum `EImageVariables`, we still need to use the combination `keyof typeof` like for a regular constant object:

```typescript
type ImageFields = keyof typeof EImageVariables
```

That's it. The above code yields the same result as when we use `ImageVariables`. 

Now let's take a recap of how we get the type from keys and values of a constant object:

 ```typescript
export const ImageVariables = {
  width: 'w',
  height: 'h',
  aspectRatio: 'ar',
  rotate: 'a',
  opacity: 'o',
} as const;

type ImageVariablesType = typeof ImageVariables;
type ImageFields = keyof ImageVariablesType;
type VariableValues = ImageVariablesType[ImageFields];
```

In comparison with using an enum:

```typescript
enum EImageVariables {
  width = 'w',
  height = 'h',
  aspectRatio = 'ar',
  rotate = 'a',
  opacity = 'o',
}

type EImageVariablesType = typeof EImageVariables;
type EImageFields = keyof EImageVariablesType;
//No need for getting the type of values
```

And same as constant objects, we can use the values of the enum's keys directly, such as `EImageVariables.width` in our code. In the runtime, enums exist in your compiled code as JavaScript Objects and behave like one. 

In general, enums are okay. In TypeScript, due to the non-efficient way it was implemented (hopefully, this has been fixed or will be short), many argue its impact on performance.

So should we use them? It depends. The choice is up to you. 

## Summary

We often ignore the type operators like `typeof` or `keyof` when using TypeScript since they are too primary. However, they play an essential role in building your type system. They can help develop advanced and complex types for your application when combined correctly and with other TypeScript syntax. Let's give them a try and see what they can bring you.

👉 _If you'd like to catch up with me sometimes, follow me on [Twitter](https://twitter.com/MayaShavin) | [Facebook](https://www.facebook.com/mayashavin/)._

Like this post or find it helpful? Share it 👇🏼 😉