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.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare