TypeScript Generics

Introduction

In this chapter, we will explore generics in TypeScript. Generics allow you to write flexible, reusable functions and classes that can work with different types while maintaining type safety. Understanding how to use generics is essential for writing robust and reusable TypeScript code.

Table of Contents

  • Definition
  • Generic Functions
  • Generic Classes
  • Generic Interfaces
  • Generic Constraints
  • Using Multiple Type Variables
  • Complete Example with Output
  • Conclusion

Definition

Generics provide a way to create reusable components that can work with a variety of types instead of a single one. By using generics, you can create functions, classes, and interfaces that can operate with different data types while ensuring type safety.

Generic Functions

Definition

A generic function is a function that can operate on different types of data without sacrificing type safety. You define a generic function by adding a type parameter in angle brackets (<T>) before the function’s parameters.

Syntax

function functionName<T>(parameter: T): T { // function implementation } 

Example

This example demonstrates a generic function that returns the input value.

function identity<T>(value: T): T { return value; } console.log(identity<number>(42)); // Output: 42 console.log(identity<string>("Hello")); // Output: Hello 

Output

42 Hello 

Generic Classes

Definition

A generic class is a class that can operate on different types of data. You define a generic class by adding a type parameter in angle brackets (<T>) after the class name.

Syntax

class ClassName<T> { // class implementation } 

Example

This example demonstrates a generic class that stores a value and provides methods to get and set the value.

class Box<T> { private value: T; constructor(value: T) { this.value = value; } getValue(): T { return this.value; } setValue(value: T): void { this.value = value; } } let numberBox = new Box<number>(123); console.log(numberBox.getValue()); // Output: 123 let stringBox = new Box<string>("Hello"); console.log(stringBox.getValue()); // Output: Hello 

Output

123 Hello 

Generic Interfaces

Definition

A generic interface is an interface that can operate on different types of data. You define a generic interface by adding a type parameter in angle brackets (<T>) after the interface name.

Syntax

interface InterfaceName<T> { // interface implementation } 

Example

This example demonstrates a generic interface that defines a structure for storing a value and methods to get and set the value.

interface Container<T> { getValue(): T; setValue(value: T): void; } class GenericContainer<T> implements Container<T> { private value: T; constructor(value: T) { this.value = value; } getValue(): T { return this.value; } setValue(value: T): void { this.value = value; } } let numberContainer = new GenericContainer<number>(456); console.log(numberContainer.getValue()); // Output: 456 let stringContainer = new GenericContainer<string>("World"); console.log(stringContainer.getValue()); // Output: World 

Output

456 World 

Generic Constraints

Definition

Generic constraints allow you to restrict the types that can be used with a generic function, class, or interface. You define a generic constraint using the extends keyword.

Syntax

function functionName<T extends Constraint>(parameter: T): T { // function implementation } 

Example

This example demonstrates a generic function that works with objects that have a length property.

interface Lengthwise { length: number; } function logLength<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; } console.log(logLength("Hello")); // Output: 5 console.log(logLength([1, 2, 3])); // Output: 3 // console.log(logLength(123)); // Error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'. 

Output

5 3 

Using Multiple Type Variables

Definition

You can use multiple type variables in a generic function, class, or interface to work with multiple types simultaneously.

Syntax

function functionName<T, U>(param1: T, param2: U): void { // function implementation } 

Example

This example demonstrates a generic function that works with two different types.

function pair<T, U>(first: T, second: U): [T, U] { return [first, second]; } let result = pair<number, string>(123, "Hello"); console.log(result); // Output: [123, 'Hello'] 

Output

[123, 'Hello'] 

Complete Example with Output

In this section, we will combine the examples into a single TypeScript file, compile it to JavaScript, and run it to see the output.

TypeScript Code

You can test the following code in the TypeScript Playground:

// Generic Functions function identity<T>(value: T): T { return value; } console.log(identity<number>(42)); // Output: 42 console.log(identity<string>("Hello")); // Output: Hello // Generic Classes class Box<T> { private value: T; constructor(value: T) { this.value = value; } getValue(): T { return this.value; } setValue(value: T): void { this.value = value; } } let numberBox = new Box<number>(123); console.log(numberBox.getValue()); // Output: 123 let stringBox = new Box<string>("Hello"); console.log(stringBox.getValue()); // Output: Hello // Generic Interfaces interface Container<T> { getValue(): T; setValue(value: T): void; } class GenericContainer<T> implements Container<T> { private value: T; constructor(value: T) { this.value = value; } getValue(): T { return this.value; } setValue(value: T): void { this.value = value; } } let numberContainer = new GenericContainer<number>(456); console.log(numberContainer.getValue()); // Output: 456 let stringContainer = new GenericContainer<string>("World"); console.log(stringContainer.getValue()); // Output: World // Generic Constraints interface Lengthwise { length: number; } function logLength<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; } console.log(logLength("Hello")); // Output: 5 console.log(logLength([1, 2, 3])); // Output: 3 // console.log(logLength(123)); // Error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'. // Using Multiple Type Variables function pair<T, U>(first: T, second: U): [T, U] { return [first, second]; } let result = pair<number, string>(123, "Hello"); console.log(result); // Output: [123, 'Hello'] 

Conclusion

In this chapter, we covered generics in TypeScript, including generic functions, classes, and interfaces, as well as generic constraints and using multiple type variables. We provided a complete example with its output to illustrate how these concepts work in TypeScript. Understanding generics is essential for writing flexible and reusable TypeScript code that maintains type safety.

Leave a Comment

Scroll to Top