Having Fun with TypeScript: Record Type
Taming the Object Jungle
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:
- Type Aliases: We define two types,
Fruit
andColor
, using union types to represent the possible fruits and colors. - Record Definition: The
fruitColors
object is defined as aRecord<Fruit, Color>
. This means its keys must be of typeFruit
and its values of typeColor
. - Valid vs. Invalid: The example shows how TypeScript will enforce the
Fruit
andColor
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!