TypeScript - Keyof Type Operator



In TypeScript, the keyof type operator allows you to obtain the keys of an object and use them to perform various operations. It plays a significant role when working with objects and their properties.

Syntax

The syntax of the keyof type operator in TypeScript is as follows –

keyof Type

The keyof keyword is followed by the name of a type, referred to as "Type." It returns a union type consisting of all the keys (property names) of the specified type. This allows you to access and manipulate an object's keys dynamically.

Examples

Let's understand the keyof operator with the help of some examples in TypeScript.

Example 1: Accessing Object Property Names

The keyof keyword helps us retrieve the names of properties defined within an object type.

In this example, we define an interface called "Person" with two properties: "name" of type string and "age" of type number.

Then we create a type alias "PersonKeys" using keyof to extract the keys from the Person interface.

Finally, we assign the value 'name' to a constant named "keys" of type PersonKeys and log its value.

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

type PersonKeys = keyof Person;
const keys: PersonKeys = 'name';
console.log(keys);

On compiling, it will generate the following JavaScript code −

var keys = 'name';
console.log(keys);

Output

The above code will produce the following output −

name

Example: Type-Safe Property Access

The keyof keyword enables us to access object properties in a type-safe manner.

In this example, we define an interface "Car" with properties representing a car's brand, year, and price. We declare a generic function called "getProperty" that takes two arguments: "obj" of type T and "key" of type K, where K extends keyof T. The function returns the value corresponding to the given key in the object. We then create a car object and use the "getProperty" function to retrieve the car's brand property and assign it to the "carBrand" variable. Finally, we log the value of "carBrand”.

interface Car {
   brand: string;
   year: number;
   price: number;
}

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
   return obj[key];
}

const car: Car = {
   brand: 'Toyota',
   year: 2022,
   price: 25000,
};

const carBrand: string = getProperty(car, 'brand');
console.log(carBrand);

On compiling, it will generate the following JavaScript code −

function getProperty(obj, key) {
   return obj[key];
}
var car = {
   brand: 'Toyota',
   year: 2022,
   price: 25000
};
var carBrand = getProperty(car, 'brand');
console.log(carBrand);

Output

The above code will produce the following output −

Toyota

Example 3: Mapping Object Properties

The keyof keyword is also useful for mapping properties from one object to another. In this example, we define an interface "Fruit" with properties representing the name and color of the fruit. We then create a new type, "FruitMapped" using the keyof within a mapped type. It maps each key in keyof Fruit to the type "string". Finally, we create a "fruit" object using the FruitMapped type and log its value.

interface Fruit {
   name: string;
   color: string;
}

type FruitMapped = {
   [K in keyof Fruit]: string;
};

const fruit: FruitMapped = {
   name: 'Apple',
   color: 'Red',
};

console.log(fruit);

On compiling, it will generate the following JavaScript code −

var fruit = {
   name: 'Apple',
   color: 'Red'
};
console.log(fruit);

Output

The above code will produce the following output −

{ name: 'Apple', color: 'Red' }

Example 4: Conditional Type Mapping

Another powerful application of the keyof keyword is in conditional type mapping. This allows us to selectively map properties based on certain conditions. In this example, we have an interface "Product" representing a product's name, price, and availability. We define a type "DiscountedProducts" that utilizes conditional mapping using keyof. It checks if the property's value extends the "number" type and maps it to the union type of the original value and "null". For all other property types, it keeps the original value type unchanged. Finally, we create a "product" object using the DiscountedProducts type.

interface Product {
   name: string;
   price: number;
   inStock: boolean;
}

type DiscountedProducts<T> = {
   [K in keyof T]: T[K] extends number ? T[K] | null : T[K];
};

const product: DiscountedProducts<Product> = {
   name: 'Widget',
   price: 10,
   inStock: true,
};

console.log(product);

On compiling, it will generate the following JavaScript code −

var product = {
   name: 'Widget',
   price: 10,
   inStock: true
};
console.log(product);

Output

The above code will produce the following output −

{ name: 'Widget', price: 10, inStock: true }
Advertisements