KeyOf and Typeof in TypeScript

TypeScript

readTime

5 min

KeyOf and Typeof in TypeScript

In this post, I’ll discuss keyof, typeof, mapped types, and conditional types, and show you how to use them in your daily work.

Introduction to Types in TypeScript

TypeScript (TS) is a superset of JavaScript that adds static typing to the language.

This means you can type your variables, functions, and objects, helping to catch errors at compile time.

But TypeScript is more than just basic types like strings or numbers. It also allows advanced typing techniques, such as keyof, typeof, mapped types, and conditional types.

Understanding Keyof in TypeScript

What is Keyof?

The keyof keyword is an operator in TypeScript that returns a union of strings representing all keys of a given object type.

How to Use Keyof?

Suppose you have an interface:

typescript
interface User {
  id: number;
  name: string;
  email: string;
}

When you use keyof User, you get a type equivalent to 'id' | 'name' | 'email'.

Example with Keyof

typescript
type UserKeys = keyof User; // 'id' | 'name' | 'email'

let key: UserKeys;

key = "name"; // OK
key = "address"; // Error: Type '"address"' is not assignable to type 'UserKeys'

With keyof, you can create more advanced types that ensure you only use the available keys of an object.

What is Typeof in JavaScript?

In JavaScript, we have the typeof operator, which returns a variable's type as a string, such as 'string', 'number'.

In TypeScript, the typeof operator can be used in typing contexts to read the type of a variable or object.

How to Use Typeof in TypeScript?

If you have a variable:

typescript
let user = {
  id: 1,
  name: "Alice",
  email: "alice@example.com",
};

You can create a type based on this variable’s type:

typescript
type UserType = typeof user;

Now UserType has exactly the same type as user.

Example with Typeof

typescript
function getUser(): UserType {
  return {
    id: 2,
    name: "Bob",
    email: "bob@example.com",
  };
}

typeof allows you to assign the type of an existing variable to a new type.

What are Mapped Types in TypeScript?

Mapped types are types that allow you to map one type to another, transforming its properties.

Using Mapped Types: Partial, Required, Readonly, Pick, Omit

TypeScript provides several built-in utility types that are mapped types:

  • Partial<T>: Makes all properties of type T optional.
  • Required<T>: Makes all properties of type T required.
  • Readonly<T>: Makes all properties of type T read-only.
  • Pick<T, K>: Creates a type with a subset of properties K from type T.
  • Omit<T, K>: Creates a type from T without the properties K.

Sample Mapped Type

Partial

typescript
type PartialUser = Partial<User>;

// All properties are optional
let user1: PartialUser = { name: "Charlie" };

Required

typescript
type RequiredUser = Required<User>;

// All properties are required
let user2: RequiredUser = {
  id: 3,
  name: "Dana",
  email: "dana@example.com",
};

Pick

typescript
type UserNameAndEmail = Pick<User, "name" | "email">;

// Type with only 'name' and 'email'
let user3: UserNameAndEmail = {
  name: "Eve",
  email: "eve@example.com",
};

Omit

typescript
type UserWithoutEmail = Omit<User, "email">;

// Type with all properties except 'email'
let user4: UserWithoutEmail = {
  id: 4,
  name: "Frank",
};

Creating Custom Mapped Types

You can also create custom mapped types:

typescript
type MyMappedType<T> = {
  [P in keyof T]: string;
};

// All properties of type T will be of type string
type StringifiedUser = MyMappedType<User>;

let user5: StringifiedUser = {
  id: "5",
  name: "Grace",
  email: "grace@example.com",
};

Advanced Typing with Conditional Types

Understanding Conditional Types

Conditional types allow you to create types that depend on certain conditions.

The syntax is similar to the ternary operator in JavaScript:

typescript
T extends U ? X : Y

If T is a subtype of U, the result is X; otherwise, it is Y.

Example with Conditional Types

typescript
type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

Uses of Conditional Types

Conditional types are very useful in the context of creating generic types that can change depending on the provided parameters.

Combining Keyof, Typeof, and Mapped Types

Creating a Type Based on Object Keys

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

let user6 = { id: 6, name: "Hank", email: "hank@example.com" };

let userName = getProperty(user6, "name"); // Type string

Here, we used keyof to restrict the key K to existing keys of the object T.

Using Typeof with Keyof

typescript
const COLORS = {
  red: "#FF0000",
  green: "#00FF00",
  blue: "#0000FF",
};

type ColorKeys = keyof typeof COLORS; // 'red' | 'green' | 'blue'

typeof lets us create a type based on COLORS, and keyof extracts the keys of that type.

Summary

Learning keyof and typeof in TypeScript opens the door to more advanced typing techniques.

Mapped types, such as Partial, Required, Pick, or Omit, facilitate type manipulation and create more flexible code.

Conditional types add another level of dynamism, allowing for condition-dependent typing.

I hope this article has helped you understand these powerful tools in TypeScript. I encourage you to experiment and create your own solutions. If you have any questions or would like to share your experiences, leave a comment!

authorImg

Witek Pruchnicki

I passionately share knowledge about programming and more in various ways.

TypeScript

More posts