Having Fun with TypeScript: Record Type

Taming the Object Jungle

Dagang Wei
3 min readApr 4, 2024

This blog post is part of the series Having Fun with TypeScript.

Introduction

In the wild world of JavaScript objects can sometimes feel like untamed beasts. Properties can appear and disappear with reckless abandon, and their types can be a mystery. TypeScript, the superhero of type safety, swoops in to bring order to the chaos with its Record type. But what exactly is this record type, and why should you care?

What is the Record Type?

The Record type in TypeScript allows you to define objects with a fixed set of keys and a specific type for both the keys and the values. Think of it as a blueprint for your objects, ensuring consistency and catching errors early on. Here's the basic syntax:

Record<Keys, Type>
  • Keys: This represents the type of the object's keys. It can be a string, number, symbol, or even another type!
  • Type: This defines the type of the object's values. Numbers, strings, booleans, or even custom types can be used here.

Why Use Record Types?

There are several reasons to embrace record types:

  • Type Safety: By specifying key and value types, you prevent unexpected data types from sneaking into your objects. This leads to fewer runtime errors and a more predictable codebase.
  • Improved Readability: Record types make your code more self-documenting. Anyone looking at your code can instantly understand the structure and expected data types of your objects.
  • Better Maintainability: Refactoring code with record types is a breeze. Changes to the object structure or types are clear and easy to track.

Code Examples in Action

Let’s see the record type in action with some examples:

Simple String-Number Dictionary

const person: Record<string, number> = {
age: 30,
height: 180,
};

// This is okay, salary is a string.
person.salary = 5000;

// Error: "John" is not a number
person.name = "John";

This code defines a person object with keys of type string (e.g., "age", "height") and values of type number. TypeScript will prevent you from accidentally assigning a non-string key or a non-number value.

Union Types for Keys and Values

Here’s an example where keys are fruit names, values are colors, and both key and value types are unions:

type Fruit = "banana" | "apple" | "orange";
type Color = "yellow" | "red" | "orange" | "green";

const fruitColors: Record<Fruit, Color> = {
banana: "yellow",
apple: "red", // Could also be 'green' for certain types of apples
orange: "orange"
};

// Valid Assignments:
fruitColors.apple = "green";

// Invalid Assignments (TypeScript will catch these):
fruitColors.pear = "yellow"; // Error: pear is not a valid Fruit
fruitColors.banana = "blue"; // Error: blue is not a valid Color

Explanation:

  1. Type Aliases: We define two types, Fruit and Color, using union types to represent the possible fruits and colors.
  2. Record Definition: The fruitColors object is defined as a Record<Fruit, Color>. This means its keys must be of type Fruit and its values of type Color.
  3. Valid vs. Invalid: The example shows how TypeScript will enforce the Fruit and Color types to prevent unexpected assignments.

Note that the Record type will ensure all keys are provided:

type Fruit = "banana" | "apple" | "orange";
type Color = "yellow" | "red" | "orange" | "green";

// Property 'apple' is missing in type { banana: "yellow"; orange: "orange"; }
// but required in type Record<Fruit, Color>.
const fruitColors: Record<Fruit, Color> = {
banana: "yellow",
orange: "orange"
};

Using an Enum for Keys

enum ProductStatus {
Available,
OutOfStock,
Discontinued,
}

const productInventory: Record<ProductStatus, number> = {
[ProductStatus.Available]: 100,
[ProductStatus.OutOfStock]: 0,
[ProductStatus.Discontinued]: 10,
};

Here, we create an enum called ProductStatus to define the possible values for our object's keys. This ensures type safety and makes the code more readable.

Summary

The record type is a powerful tool in your TypeScript arsenal. By leveraging it, you can create well-defined, type-safe objects that make your code more maintainable, readable, and less prone to errors. So, next time you’re working with objects in TypeScript, consider harnessing the power of record types to bring order to your object zoo!

--

--