A brief discussion on the understanding of TypeScript index signatures

A brief discussion on the understanding of TypeScript index signatures

We use two objects to describe the salaries of two coders:

const salary1 = {
  baseSalary: 100_000,
  yearlyBonus: 20_000
};
 
const salary2 = {
  contractSalary: 110_000
};


Then write a function to get the total salary

function totalSalary(salaryObject: ???) {
  let total = 0;
  for (const name in salaryObject) {
    total += salaryObject[name];
  }
  return total;
}
totalSalary(salary1); // => 120_000
totalSalary(salary2); // => 110_000


If it were you, how would you declare the salaryObject parameter of totalSalary() function to accept an object with string keys and numeric values?

The answer is to use an index signature!

Next, let’s look at what TypeScript index signatures are and when they are needed.

1. What is an index signature?

The idea of ​​index signatures is to type objects whose structure is unknown when only the key and value types are known.

It fits perfectly in the case of salary parameter, as the function should accept salary objects of different structures, the only requirement being that the attribute values ​​are numbers.

We declare salaryObject parameter with an index signature

function totalSalary(salaryObject: { [key: string]: number }) {
  let total = 0;
  for (const name in salaryObject) {
    total += salaryObject[name];
  }
  return total;
}
 
totalSalary(salary1); // => 120_000
totalSalary(salary2); // => 110_000


{[key: string]: number} is the index signature, which tells TypeScript salaryObject must be an object with string as key and number as value.

2. Index signature syntax

The syntax for index signatures is fairly straightforward and looks similar to the syntax for properties, but there is one difference. We just write the key type inside square brackets instead of the property name: { [ key: KeyType]: ValueType }.

Below are some examples of index signatures.

string type is the key and value.

interface StringByString {
  [key: string]: string;
}
 
const heroesInBooks: StringByString = {
  'Gunslinger': 'Front-end Wisdom',
  'Jack Torrance': 'Wang Dazhi'
};


The string type is the key, and the value can be string , number or boolean

interface Options {
  [key: string]: string | number | boolean;
  timeout: number;
}
 
const options: Options = {
  timeout: 1000,
  timeoutMessage: 'The request timed out!',
  isFileUpload: false
};

The signature key can only be a string `, number or symbol `. Other types are not allowed.

3. Notes on index signatures

There are some caveats with index signatures in TypeScript that you need to be aware of.

3.1 Non-existent properties

What happens if you try to access a non-existent property of an object with an index signature of { [key: string]: string }?

As expected, TypeScript infers the type of the value to be string . But checking the runtime value, it is undefined :

According to TypeScript , the value variable is of type string , but its runtime value is undefined .

An index signature simply maps a key type to a value type, nothing more. If this mapping is not done correctly, the value type may deviate from the actual runtime data type.

To make the input more accurate, the index value is marked as string or undefined . This way, TypeScript will realize that the property you are accessing may not exist.

3.2 string and number keys

Suppose there is a dictionary of number names:

interface NumbersNames {
  [key: string]: string
}
 
const names: NumbersNames = {
  '1': 'one',
  '2': 'two',
  '3': 'three',
  // ...
};

No, it works normally.

JavaScript implicitly coerces numbers to strings when used as keys in property accessors ( names[1] is the same as names['1' ]). TypeScript also enforces this.

You can think of [key: string ] as the same as [key: string | number] .

4. Index signature vs. Record<Keys, Type>

TypeScript has a utility type Record<Keys, Type>, is similar to index signatures.

const object1: Record<string, string> = { prop: 'Value' }; // OK
const object2: { [key: string]: string } = { prop: 'Value' }; // OK

So the question is... when do you use Record<Keys, Type>, when do you use index signatures? At first glance, they look similar

As we know, index signatures only accept string , number or symbol as key types. It is an error if you try to use, for example, a union of string literal type as a key in an index signature.

Index signatures are generic with respect to the keys.

But we can use a union of string literals to describe the keys in Record<keys, Type>

type Salary = Record<'yearlySalary'|'yearlyBonus', number>
 
const salary1: Salary = { 
  'yearlySalary': 120_000,
  'yearlyBonus': 10_000
}; // OK


Record<Keys, Type> is for key-specific issues.

It is recommended to annotate generic objects with index signatures, e.g. the key is of type string. However, when you know the keys in advance, use Record<Keys, Type> to annotate specific objects, such as string literals ' prop1' | 'prop2' being used for the keys.

Summarize:

If you don't know the object structure you're dealing with, but you know the possible key and value types, then index signatures are what you need.

An index signature consists of the index name and its type in square brackets, followed by a colon and the value type: { [indexName: KeyType]: ValueType }, KeyType can be a string , number , or symbol , and ValueType can be any type.

This concludes this article on the understanding of TypeScript index signatures. For more related TypeScript index signature content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed steps to build the TypeScript environment and deploy it to VSCode
  • A tutorial on how to install, use, and automatically compile TypeScript
  • Debug static pages with typescript in .net6 using vs2022

<<:  Mysql implements regular clearing of old data in a table and retaining several pieces of data (recommended)

>>:  Solve the problem that Docker cannot ping the host machine under Mac

Recommend

An article to understand the use of proxies in JavaScript

Table of contents What is an agent Basic knowledg...

Summary of WEBAPP development skills (notes for mobile website development)

1. To develop web responsively, the page must ada...

Specific example of MySQL multi-table query

1. Use the SELECT clause to query multiple tables...

5 common scenarios and examples of JavaScript destructuring assignment

Table of contents Preface 1. Extract data 2. Alia...

Detailed explanation of software configuration using docker-compose in linux

Preface This article will share some docker-compo...

Summary of examples of common methods of JavaScript arrays

Table of contents Common array methods concat() M...

Thirty HTML coding guidelines for beginners

1. Always close HTML tags In the source code of p...

How to use Maxwell to synchronize MySQL data in real time

Table of contents About Maxwell Configuration and...

harborRestart operation after modifying the configuration file

I won't say much nonsense, let's just loo...

Vue implements real-time refresh of the time display in the upper right corner

This article example shares the specific code of ...

js implements the algorithm for specifying the order and amount of red envelopes

This article shares the specific code of js to im...

Details of various font formats in HTML web pages

This section starts with the details of text modi...

Dockerfile text file usage example analysis

Dockerfile is a text file used to build an image....

Do you know how many connections a Linux server can handle?

Preface First, let's see how to identify a TC...