Contents

Programming Fundamentals › Type Systems

Generics

Code that works over many types while keeping type safety.

Also known as: generic types, type parameters, generic functions, templates, parametric polymorphism

Generics let you write code that works with many types while keeping the type safety. Instead of fixing a type, you leave a placeholder (a type parameter, usually T) that’s filled in when the code is used.

Without generics you pick between a type too narrow or one too loose:

function firstNumber(items: number[]): number | undefined { return items[0]; }   // only numbers
function firstAny(items: any[]): any { return items[0]; }                        // loses type information

With a generic:

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const n = first([1, 2, 3]);          // n: number | undefined
const s = first(["a", "b"]);         // s: string | undefined

The type flows through: put in strings, get strings out. The compiler usually infers T itself (type inference).

Where you see them

const ids: Array<number> = [];                 // Array<T>
const lookup: Map<string, User> = new Map();   // Map<K, V>
const p: Promise<Response> = fetch(url);       // Promise<T>

interface ApiResponse<T> { data: T; error?: string }
function get<T>(url: string): Promise<ApiResponse<T>> { ... }
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T | None:
    return items[0] if items else None

Java, C#, Rust, Go (since version 1.18) and others have generics, with different details.

Constraints

Sometimes the function needs the type to have certain abilities:

function byId<T extends { id: number }>(items: T[], id: number): T | undefined {
  return items.find(item => item.id === id);
}

T extends { id: number } means “any type that has a numeric id”.

Guidelines

  • Use generics when the same logic applies to different types and the relationship between input and output types matters.
  • Don’t over-engineer. A generic with five type parameters and nested conditions is hard to read. If a plain type or a union works, use it.
  • Name type parameters meaningfully when there are several (TKey, TValue, TItem).
  • Variance (how generic types relate when their parameters are related) is a deeper topic (variance).