What is the significance of using 'keyof typeof' in TypeScript?

Can you please explain the concept of keyof typeof in TypeScript?

For example:

enum ColorsEnum {
    white = '#ffffff',
    black = '#000000',
}

type Colors = keyof typeof ColorsEnum;

The above code is essentially equivalent to:

type Colors = "white" | "black"

But how does it actually work?

One might assume that typeof ColorsEnum would return something like "Object", and then keyof "Object" wouldn't provide any interesting results. However, this assumption proves to be incorrect.

Answer №1

To comprehend the utilization of keyof typeof in TypeScript, it is crucial to grasp the concept of literal types and union of literal types first. Let's delve into these concepts before delving into a detailed explanation of keyof and typeof. Subsequently, we will revisit enum to address the question at hand. Although lengthy, the examples provided are straightforward for better understanding.


Literal Types

In TypeScript, literal types are more specific variations of string, number, or boolean. For instance, while "Hello World" is a string, a generic string does not equate to "Hello World". Hence, "Hello World" qualifies as a type of its own within the string category—a literal type.

A declaration of a literal type looks like this:

type Greeting = "Hello"

This signifies that an object of type Greeting can exclusively hold the value "Hello", rejecting any other string value or values of different types, as demonstrated below:

let greeting: Greeting
greeting = "Hello" // Works fine
greeting = "Hi"    // Error: Type '"Hi"' cannot be assigned to type '"Hello"'

While standalone, literal types may seem limited, their potency amplifies when merged with union types, type aliases, and type guards.

Below is an illustration of a union of literal types:

type Greeting = "Hello" | "Hi" | "Welcome"

Consequently, an object of type Greeting embodies either "Hello", "Hi", or "Welcome".

let greeting: Greeting
greeting = "Hello"       // Fine
greeting = "Hi"          // Fine
greeting = "Welcome"     // Fine
greeting = "GoodEvening" // Error: Type '"GoodEvening"' cannot be assigned to 'Greeting'

keyof Only

The keyof operator on a type T produces a fresh type comprising a union of literal types, represented by the property names within T. This resultant type is a subset of string.

Consider the subsequent interface:

interface Person {
    name: string
    age: number
    location: string
}

Employing the keyof operator on the type

Person</code} yields the ensuing type:</p>
<pre><code>type SomeNewType = keyof Person

This newly formed SomeNewType encapsulates a union of literal types (

"name" | "age" | "location"
) extracted from the properties of Person.

Objects conforming to

SomeNewType</code} can now be initiated:</p>
<pre><code>let newTypeObject: SomeNewType
newTypeObject = "name"           // Enforced
newTypeObject = "age"            // Enforced
newTypeObject = "location"       // Enforced
newTypeObject = "anyOtherValue"  // Rejected...

keyof typeof Cohesion within an Object

The typeof operator retrieves the type of an object. In the aforementioned example featuring the Person interface, knowing the type permitted us to employ the keyof operator on Person.

However, situations arise where an object's type is unknown or only a value is available without the corresponding type, exemplified here:

const bmw = { name: "BMW", power: "1000hp" }

This scenario necessitates the united application of keyof typeof.

typeof bmw identifies the type: { name: string, power: string }

Subsequently, employing the keyof operator engenders a union of literal types, depicted in the ensuing code snippet:

type CarLiteralType = keyof typeof bmw

let carPropertyLiteral: CarLiteralType
carPropertyLiteral = "name"       // Permissible
carPropertyLiteral = "power"      // Permissible
carPropertyLiteral = "anyOther"   // Rejected...

keyof typeof Utilization on an enum

Within TypeScript, enums primarily serve as compile-time types to reinforce constant type integrity. Post compilation from TypeScript to JavaScript, they become basic objects. Consequently, similar principles apply to enum objects. The following excerpt mirrors the OP's query:

enum ColorsEnum {
    white = '#ffffff',
    black = '#000000',
}

Given that ColorsEnum exists as a runtime object rather than merely a type, combining keyof typeof is indispensable:

type Colors = keyof typeof ColorsEnum

let colorLiteral: Colors
colorLiteral = "white"  // Valid
colorLiteral = "black"  // Valid
colorLiteral = "red"    // Rejected...

Answer №2

keyof is a method that takes an object type and generates a type containing all the keys of that object.

type Coordinates = { x: number; y: number };
type CoordKeys = keyof Coordinates; // type '"x" || "y"'

const point: CoordKeys = 'z' // This results in an error because Type '"z"' is not assignable to type '"x" | "y"'.

Understanding typeof in TypeScript types

When using typeof in TypeScript, it behaves differently compared to when used with JavaScript objects.

  • JavaScript's typeof in TypeScript returns values such as
    "undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function"
    when called on JavaScript values at runtime.
  • TypeScript's typeof is used for type values and can infer detailed object types when called on JavaScript values within a type expression.
type LangOption = 'EN' | 'ES'; 
const userLang: LangOption = 'EN';
const config = { language: userLang, theme: 'light' };

console.log(typeof config); // Returns "object"
type ConfigType = typeof config; // type '{language: 'EN''; theme: string; }'

The second usage of typeof config, being within a type expression, calls TypeScript's typeof instead of JavaScript's at runtime.

Exploring keyof typeof

Since keyof belongs to TypeScript, we are referring to its specific version of typeof.

keyof typeof infers the type of a JavaScript object and produces a union type consisting of its keys. It can return unions of literal types based on the exact key values.

type ConfigKeys = keyof typeof config; // type '"language" | "theme"'

Answer №3

The use of an enum results in the creation of a tangible entity. By employing typeof, we are able to access the automatically assigned type of this particular enum.

Subsequently, through utilizing keyof, all potential indices can be obtained in order to restrict Colors to only one valid option.

Answer №4

The truth about TypeScript

Many people think of TypeScript as simply a layer of types on top of JavaScript, separate from the actual values. However, in reality, some things in TypeScript are both types and values at the same time.

Specifically, this applies to:

  • classes,
  • enums,
  • namespaces.

When is it appropriate to use keyof?

The keyof keyword is designed to be used at the type level only. It cannot be applied to a JavaScript value directly.

When do you require keyof typeof?

If you are working with something that acts as both a type and a value simultaneously (such as a class or an enum), but you specifically want to focus on the type aspect of that value.

For example:

const foo = { bar: 42 };
type Foo = typeof foo;

type KeyOfFoo = keyof Foo; // "keyof Foo" equals "keyof typeof foo", which results in "bar"

In essence, when you encounter this:

type A = keyof typeof B;

The typeof B segment instructs TypeScript to consider the type of B. This can be likened to converting B into its type representation—a bit like transforming a two-dimensional object into a one-dimensional form.

Due to the fact that typeof B represents a type rather than a value, we can now utilize keyof on it.

An Illustrative Example

Classes in TypeScript possess characteristics of both types and values. You can instantiate them, but also apply keyof to them.

declare class Foo {
    static staticProperty: string;

    dynamicProperty: string;
}

type Constructor = typeof Foo;
type Instance = Foo;

type A = keyof Constructor; // "prototype" | "staticProperty"
type B = keyof Instance; // "dynamicProperty"

Through the combination of typeof and keyof, we have the ability to switch between utilizing keyof against the instance type or the constructor type.

Answer №5

To determine the data type of a value, we can utilize the typeof operator. For example:

const person = {
   getDetails(){},
   getLocation(){}
}

In this case, 'person' is considered a value, hence the typeof operator is useful.

type personType = typeof person

The personType provides information that 'person' is an object with two properties - getDetails and getLocation, both functions returning void.

If you wish to identify the keys of 'person', you can use keyof:

type personKeys = keyof personType

This indicates that personKeys= 'getDetails'| 'getLocation'

It's important to note that attempting to access 'person''s keys directly using type personKeys = keyof person will result in an error message: 'person' refers to a value, but is being used as a type here. Did you mean 'typeof person'?

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Filtering a key-value pair from an array of objects using Typescript

I am working with an array of objects containing elements such as position, name, and weight. const elements = [{ position: 3, name: "Lithium", weight: 6.941, ... },{ position: 5, name: "Boron", weight: 10.811, ... }, { position: 6, name: "Carbon", weight: ...

How long does it take to delete and recreate a cloudfront distribution using AWS CDK?

I am currently undergoing the process of migrating from the AWS CDK CloudfrontWebDistribution construct to the Distribution Construct. According to the documentation, the CDK will delete and recreate the distribution. I am curious about the total duration ...

Obtaining Input Field Value in Angular Using Code

How can I pass input values to a function in order to trigger an alert? Check out the HTML code below: <div class="container p-5 "> <input #titleInput *ngIf="isClicked" type="text" class="col-4"><br& ...

Encountering a 403 status code from the Spotify Web API while attempting to retrieve data using an OAuth 2.0 Token

I'm currently experimenting with the Spotify Web API and attempting to retrieve my most played songs. To obtain an access token for making requests, I am utilizing the client credentials OAuth flow. While I have successfully obtained the access token, ...

Tips for managing the output of an asynchronous function in TypeScript

The casesService function deals with handling an HTTP request and response to return a single object. However, due to its asynchronous nature, it currently returns an empty object (this.caseBook). My goal is for it to only return the object once it has b ...

Use JavaScript's Array.filter method to efficiently filter out duplicates without causing any UI slowdown

In a unique case I'm dealing with, certain validation logic needs to occur in the UI for specific business reasons[...]. The array could potentially contain anywhere from several tens to hundreds of thousands of items (1-400K). This frontend operation ...

Using Angular 2 with the firebase-admin sdk

Whenever I attempt to integrate the firebase-admin SDK into my Angular2 project, an error occurs: ERROR in ./~/firebase-admin/lib/auth/token-generator.js Module not found: Error: Can't resolve 'jsonwebtoken' in '/home/koucky/git_projec ...

Issues arise when attempting to use recursive types in combination with optional properties

In my code, I've created a type definition that allows me to traverse an object using an array of strings or indices representing the keys of the object or nested arrays: export type PredicateFunction<ArrayType> = (array: ArrayType, index?: numb ...

Set a field to mandatory or optional depending on a condition in serenity-platform

Currently, I am tackling a project on the serenity platform. Does anyone have suggestions on how to dynamically change the field's required status based on a condition within the AbcDialog.ts file? Thank you! ...

The data type '{ [key: string]: number; }' cannot be assigned to type 'T'

I’m working with a complex TypeScript type and trying to manage it within a function. Here’s what I have: type T = { a: number } | { b: number } function func(k: 'a' | 'b', v: number) { // error message below const t: T = { ...

What is the proper way to define a new property for an object within an npm package?

Snippet: import * as request from 'superagent'; request .get('https://***.execute-api.eu-west-1.amazonaws.com/dev/') .proxy(this.options.proxy) Error in TypeScript: Property 'proxy' is not found on type 'Super ...

Unable to set a breakpoint within Angular constructor or OnInit method

I am currently facing an issue with my Angular application where breakpoints set in F12 tools in Chrome or IE are not working. I have a simple test case below: export class LoginComponent implements OnInit { message: string; constructor(private r ...

Control the transparency of the initial parent div that includes an *ngFor loop

I want to add opacity only to the first div which contains an icon and a heading in another nested div. The second div should remain fully visible (opacity: 1). Here is the HTML structure: <div class="row clearfix"> <div class="col-lg-3 col- ...

Loading text not displaying in Angular 2 mobile app

I have developed an angular2 application using typescript that relies on SystemJS. My starting point was this seed app I found. When viewed on a desktop, you can observe the loading text enclosed within tags (e.g. Loading...). On the index page of my app ...

Using Typescript, invoke functions within an object by specifying a string key

I am looking for a way to call methods in an Object using string keys, but I'm facing issues with it. Could someone provide some solutions for this? type Methods = { foo?: (v: string) => string; bar?: (v: number) => number; baz?: (v ...

Converting an array with objects to comma separated strings in javascript using (ionic , Angular , NodeJS , MongoDB)

Retrieving specific data from an API array: let passions = [ {_id: "60a1557f0b4f226732c4597c",name: "Netflix"}, {_id: "60a1557f0b4f226732c4597b",name: "Movies"} ] The desired output should be: ['60a1557f0b4f226 ...

Creating a way for a Node and React application to share classes

I am in the process of developing a Node and React application, both implemented using TypeScript. The directory structure of my project is illustrated below: https://i.sstatic.net/914A1.png My query: Given that I am utilizing the same programming langu ...

What could be causing the issue where only one of my videos plays when hovered over using UseRef?

I'm currently working on a project where I have a row of thumbnails that are supposed to play a video when hovered over and stop when the mouse moves out of the thumbnail. However, I've encountered an issue where only the last thumbnail plays its ...

Incorporating and modifying a component's aesthetics using styled-components: A comprehensive guide

My OverviewItem component has 2 props and is a styled-component. I want to change just one style on this component, which can be done by using the technique of styling any component. Does creating a wrapper component remain the only option for sharing st ...

Issue: The 'draggable' property is not found on the 'JQuery<HTMLElement>' type

When using Angular 2 and Typescript in conjunction with JQuery and JQueryUI, I encountered the following error: Property 'draggable' does not exist on type 'JQuery<HTMLElement>' I am aware that .draggable() is a function that rel ...