avatarBytefer

Summary

The article discusses strategies for defining objects with unknown structures in TypeScript, offering alternatives to the any type for more precise type definitions.

Abstract

The article "How To Define Objects Type With Unknown Structures in TypeScript" is part of the "Mastering TypeScript" series, which aims to teach TypeScript through animated explanations. It addresses a common challenge faced by TypeScript learners: defining types for objects with dynamic or unknown structures. The author presents three solutions to handle this issue: using the any type, defining custom types with interface or type along with index signatures, and utilizing TypeScript's built-in Record utility type. The article emphasizes the importance of avoiding the any type to prevent type safety issues and demonstrates how index signatures and the Record type can be used to define types for objects that may have additional properties. It also touches on the nuances of index signatures, such as JavaScript's implicit coercion of numbers to strings when used as object keys, and the differences between index signatures and the Record type in terms of key restrictions.

Opinions

  • The author suggests that using the any type is a "violent" approach to type definition, implying it should be avoided due to its potential to undermine TypeScript's type safety.
  • The article promotes the use of interface or type with index signatures as a more robust solution for defining objects with unknown structures.
  • It highlights the flexibility of index signatures in TypeScript, which allow for the definition of types with dynamic properties.
  • The author recommends the Record utility type as an alternative to index signatures, especially when dealing with keys that are literal types or unions of literal types.
  • The article encourages readers to familiarize themselves with TypeScript's mapped types, which are foundational to understanding how the Record utility type and other built-in utility types function.
  • By providing links to related articles, the author conveys the value of a comprehensive understanding of TypeScript's advanced features, such as template literal types and mapped types, to write better code.
  • The author invites readers to follow their work on Medium and Twitter for more insights into TypeScript and JavaScript, indicating a commitment to community education and engagement.

How To Define Objects Type With Unknown Structures in TypeScript

A problem that most people encounter when learning TypeScript, how many solutions can you think of?

Photo by Greg Rakozy on Unsplash

Welcome to the Mastering TypeScript series. This series will introduce the core knowledge and techniques of TypeScript in the form of animations. Let’s learn together! Previous articles are as follows:

Did you encounter similar errors when you were learning TypeScript?

To fix this error, a very violent way is to use any type:

let user: any = {}
user.id = "TS001";
user.name = "Bytefer";

Besides using any type, how many solutions do you know? In this article, I will introduce 3 other solutions. Before you continue reading, I suggest you take a moment to think about it.

Photo by Aron Visuals on Unsplash

One of the solutions is to use type or interface to define a User type:

interface User {
  id: string;
  name: string;
}
let user = {} as User;
user.id = "TS001";
user.name = "Bytefer";

Although using the User type, the previous problem can be solved. But if you set a new age property for the user object, the following error message will be displayed:

Property 'age' does not exist on type 'User'.ts(2339)

So how should we solve the problem of dynamic property assignment? At this point, we can use TypeScript’s index signatures. When we only know the type of the object keys and values, we can use index signatures to define the type of that object. The syntax of the index signatures is as follows:

The type of the key can only be string, number, symbol, or template literal type, while the type of the value can be any type.

The template literal types is a new type introduced in TypeScript 4.1, and in combination with index signatures, we can define more powerful types.

If you want to learn more about template literal types, I recommend you read this article:

Once we understand the syntax of the index signatures, we can easily define a new User type:

interface User {
  id: string;
  name: string;
  [key: string]: string;
}

Where id and name are already properties, we set the type of other properties of User type to string type by index signatures. When using index signatures, you may encounter these confusions:

  • Why can the corresponding property value be accessed through the string “1” and the number 1?
  • Why does keyof NumbersNames return a union type of string and number types?

This is because JavaScript implicitly coerces numbers to strings when used as keys in property accessors, and TypeScript performs this conversion as well.

In addition to using index signatures, we can also use TypeScript’s built-in utility type Record type to define the User type. The role of the Record utility type is as follows:

type User = Record<string, string>
let user = {} as User;
user.id = "TS001"; // Ok
user.name = "Bytefer"; // Ok

So what’s the difference between an index signatures and a Record utility type? In some cases, they all define the expected type.

const user1: Record<string, string> = { name: "Bytefer" }; // Ok
const user2: { [key: string]: string } = { name: "Bytefer" }; // Ok

For index signatures, the key type can only be string, number, symbol, or template literal type. For the Record utility type, the key type can be a literal type or a union of literal types:

To get a better grasp of the Record utility type, let’s take a look at its internal implementation:

/**
 * Construct a type with a set of properties K of type T.
 * typescript/lib/lib.es5.d.ts
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

The Record utility type uses the TypeScript mapped types internally, which is used in other built-in utility types. If you want to learn more about TypeScript mapped types, I recommend you read the following article carefully:

After reading this article, I believe you already understand TypeScript index types and Record Record utility types. If you want to learn TypeScript, then don’t miss the Mastering TypeScript series.

Follow me on Medium or Twitter to read more about TS and JS!

More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter, LinkedIn, YouTube, and Discord.

Typescript
JavaScript
Front End Development
Web Development
Programming
Recommended from ReadMedium