Understanding TypeScript Generics: Type Constraints and Extending Generics

by Nov 5, 2023typescript generics

In our journey through TypeScript generics, we’ve uncovered the basics and seen how they provide flexibility while maintaining type safety. Now, it’s time to venture into the realm of type constraints and discover how they allow us to fine-tune our generic code.

Exploring the Concept of Type Constraints

TypeScript generics are incredibly versatile, but there are scenarios where we may want to narrow down the range of possible data types. This is where type constraints come into play. Type constraints allow us to specify the types that a type parameter can accept, providing a higher degree of control and predictability.

Consider the following example:

function logValue<T>(value: T): void {
  console.log(value);
}

The <T> in the function signature indicates that logValue is a generic function, and T is a type parameter. This means that you can call the function with values of different types, and TypeScript will infer or allow you to specify the type when you call the function.

The logValue function accepts any data type, which is a typical use of generics. The void return type in the function signature indicates that the function doesn’t return a value; it only logs the input value to the console. However, what if we want to ensure that the provided value is of a specific type, like a number? This is where type constraints shine.

This generic function can be useful when you want to log values of various types without specifying the type explicitly each time you call the function.

logValue("Hello, world"); // Logs: Hello, world
logValue(42); // Logs: 42
logValue([1, 2, 3]); // Logs: [1, 2, 3]

Constraining Generic Types to Specific Types or Shapes

To apply type constraints, we can use the extends keyword, followed by the desired type, in our type parameter declaration. Let’s see how we can constrain the logValue function to accept only numbers:

function logNumber<T extends number>(value: T): void {
  console.log(value);
}

logNumber(42); // Works fine
logNumber("Hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

The above code defines a TypeScript function called logNumber, which is a generic function that takes an argument of type T, where T is constrained to be a subtype of the number type using the extends keyword. This means that you can only call logNumber with values that are numbers or subtypes of numbers.

  1. function logNumber<T extends number>(value: T): void: This function specifies that T must be a subtype of number. This constraint ensures that only numeric values can be passed to the function.
  2. logNumber(42);: This call to logNumber with the number 42 works fine because 42 is a valid argument that satisfies the constraint.
  3. logNumber("Hello");: This call to logNumber with a string "Hello" results in a TypeScript error because a string is not a subtype of number. The constraint enforces that the argument must be a number or a subtype of a number, so passing a string is not allowed.

Adding More Constraints with the extends Keyword

TypeScript allows us to apply multiple constraints to a generic type using the extends keyword. This enables us to specify a set of types that the type parameter must satisfy.

Let’s explore an example where we want a generic function that works with objects containing specific properties:

Suppose we want to create a generic function that calculates the average value of an array of numbers. However, we want to ensure that the generic function only works with arrays of numbers and not other data types. Here’s how we can achieve this using type constraints:

function calculateAverage<T extends number[]>(numbers: T): number {
  const sum = numbers.reduce((total, num) => total + num, 0);
  return sum / numbers.length;
}

function calculateAverage: This line declares a function named calculateAverage.

<T extends number[]>: This part is a generic type parameter declaration. It defines that the function is generic and can work with an array of any type T as long as that type extends the number[] type. In other words, it ensures that the numbers parameter must be an array of numbers.

(numbers: T): This specifies that the calculateAverage function takes one parameter called numbers, which is of type T.

: number {: This part of the function signature specifies that the function returns a number. The curly brace { indicates the start of the function body.

const sum = numbers.reduce((total, num) => total + num, 0);
  • const sum: This line declares a variable named sum to store the sum of the numbers in the numbers array.
  • =: This is the assignment operator, which assigns the value on the right-hand side to the variable on the left-hand side.
  • numbers.reduce((total, num) => total + num, 0);: This is an expression that calculates the sum of the numbers in the numbers array using the reduce method.
    • .reduce(): This is an array method used to reduce an array to a single value.
    • (total, num) => total + num: This is an arrow function that takes two parameters, total (the accumulator) and num (the current element in the array). It adds the current element to the accumulator, effectively calculating the sum.
    • , 0: The 0 is the initial value for the total accumulator. It initializes the sum to zero.

In this example:

  • The calculateAverage function takes an array (numbers) of type T.
  • We use the extends number[] constraint to ensure that T is an array of numbers.
  • The function calculates the average of the numbers in the array and returns it as a number.

Now, let’s see how we can use this generic function:

const numberArray = [1, 2, 3, 4, 5];
const average = calculateAverage(numberArray); // Returns the average: 3
  • Using the type constraint extends number[], we ensure that the calculateAverage function can only be called with arrays of numbers, providing type safety and preventing accidental usage with other data types.

Conclusion

This example demonstrates how you can add constraints to generics to specify the allowed data types, making your code more robust and type-safe.

Creating a multiplication Skill in Alexa using python

Written By Monisha Macharla

Hi, I'm Monisha. I am a tech blogger and a hobbyist. I am eager to learn and explore tech related stuff! also, I wanted to deliver you the same as much as the simpler way with more informative content. I generally appreciate learning by doing, rather than only learning. Thank you for reading my blog! Happy learning!

RELATED POSTS

Advanced Generics: Higher-Order Functions

Advanced Generics: Higher-Order Functions

In our journey through TypeScript generics, we've covered the basics, interfaces, and classes. Now, it's time to explore advanced concepts by combining generics with higher-order functions. These functions, which take other functions as arguments or return them,...

Basic Usage of Generics in Typescript

Basic Usage of Generics in Typescript

Keypoints Show how to declare generic functions and classes in TypeScript. Provide examples of generic functions that work with different data types. Demonstrate the use of built-in generics like Array<T> and Promise<T>. Here's the content...

VIDEOS – FOLLOW US ON YOUTUBE

EXPLORE OUR IOT PROJECTS

IoT Smart Gardening System – ESP8266, MQTT, Adafruit IO

Gardening is always a very calming pastime. However, our gardens' plants may not always receive the care they require due to our active lifestyles. What if we could remotely keep an eye on their health and provide them with the attention they require? In this article,...

How to Simulate IoT projects using Cisco Packet Tracer

In this tutorial, let's learn how to simulate the IoT project using the Cisco packet tracer. As an example, we shall build a simple Home Automation project to control and monitor devices. Introduction Firstly, let's quickly look at the overview of the software. Packet...

All you need to know about integrating NodeMCU with Ubidots over MQTT

In this tutorial, let's discuss Integrating NodeMCU and Ubidots IoT platform. As an illustration, we shall interface the DHT11 sensor to monitor temperature and Humidity. Additionally, an led bulb is controlled using the dashboard. Besides, the implementation will be...

All you need to know about integrating NodeMCU with Ubidots over Https

In this tutorial, let's discuss Integrating NodeMCU and Ubidots IoT platform. As an illustration, we shall interface the DHT11 sensor to monitor temperature and Humidity. Additionally, an led bulb is controlled using the dashboard. Besides, the implementation will be...

How to design a Wireless Blind Stick using nRF24L01 Module?

Introduction Let's learn to design a low-cost wireless blind stick using the nRF24L01 transceiver module. So the complete project is divided into the transmitter part and receiver part. Thus, the Transmitter part consists of an Arduino Nano microcontroller, ultrasonic...

Sending Temperature data to ThingSpeak Cloud and Visualize

In this article, we are going to learn “How to send temperature data to ThingSpeak Cloud?”. We can then visualize the temperature data uploaded to ThingSpeak Cloud anywhere in the world. But "What is ThingSpeak?” ThingSpeak is an open-source IoT platform that allows...

Amaze your friend with latest tricks of Raspberry Pi and Firebase

Introduction to our Raspberry Pi and Firebase trick Let me introduce you to the latest trick of Raspberry Pi and Firebase we'll be using to fool them. It begins with a small circuit to connect a temperature sensor and an Infrared sensor with Raspberry Pi. The circuit...

How to implement Machine Learning on IoT based Data?

Introduction The industrial scope for the convergence of the Internet of Things(IoT) and Machine learning(ML) is wide and informative. IoT renders an enormous amount of data from various sensors. On the other hand, ML opens up insight hidden in the acquired data....

Smart Display Board based on IoT and Google Firebase

Introduction In this tutorial, we are going to build a Smart Display Board based on IoT and Google Firebase by using NodeMCU8266 (or you can even use NodeMCU32) and LCD. Generally, in shops, hotels, offices, railway stations, notice/ display boards are used. They are...

Smart Gardening System – GO GREEN Project

Automation of farm activities can transform agricultural domain from being manual into a dynamic field to yield higher production with less human intervention. The project Green is developed to manage farms using modern information and communication technologies....