Learn TypeScript - Generics and Parametric Types
Episode 8 of 23

Learn TypeScript - Generics and Parametric Types

This episode introduces generics, the most powerful tool for writing code that works across many types. You'll learn generic functions, type parameters, constraints with extends, and generic interfaces and classes with default type parameters.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Imagine writing identical functions that handle arrays of strings, arrays of numbers, and arrays of objects. Without help, you either copy the code three times or throw away type guarantees with any. Generics solve both at once: one implementation, types still preserved.

Generics are a way to write parametric types, that is, types that accept type arguments. The analogy is with functions over values: a value function takes a value argument and returns a value, while a generic takes a type argument and produces a concrete type.

Episode 8 covers generic functions, constraints so type parameters don't run wild, generics on interfaces and classes, and default type parameters. These are the foundations for reading and writing real TypeScript libraries.

Generic Functions

Basic Type Parameters

A generic function is declared with a type parameter inside angle brackets:

Fungsi generik
function elemenPertama<T>(data: T[]): T | undefined {
    return data[0];
}
 
const angka = elemenPertama([10, 20, 30]);
const kata = elemenPertama(["a", "b"]);

The declaration function elemenPertama<T>(data: T[]): T states that the return type equals the element type of the input array. Called with a number array, the result is number. TypeScript infers T automatically, so you don't need to write the type on the calling side.

Safe Identity

The most classic example is the identity function:

Fungsi identitas
function identitas<T>(nilai: T): T {
    return nilai;
}
 
const teks = identitas("halo");
const nomor = identitas(42);

The identitas function accepts any value and returns it with the same type. Without generics, this function would have to be any and lose all type information. With generics, every call gets the precise type.

Constraints with extends

An unconstrained type parameter accepts anything. A constraint limits it to a specific shape:

Konstrain dengan extends
interface PunyaPanjang {
    length: number;
}
 
function ukuran<T extends PunyaPanjang>(input: T): number {
    return input.length;
}
 
ukuran("halo");
ukuran([1, 2, 3]);
ukuran(123);

The constraint T extends PunyaPanjang guarantees the input has a length property, so the access input.length is safe. A call with a number is rejected by the compiler because number has no length. Constraints keep the function body valid for every type it allows.

Generic Interfaces and Classes

Generics aren't just for functions. Interfaces and classes can also be parametric:

Interface dan class generik
interface Kotak<T> {
    isi: T;
}
 
class Penyimpan<T> {
    private nilai: T;
 
    constructor(nilai: T) {
        this.nilai = nilai;
    }
 
    ambil(): T {
        return this.nilai;
    }
}
 
const kunci = new Penyimpan<string>("rahasia");
const kataKunci: string = kunci.ambil();

interface Kotak<T> defines a contract whose content has type T. class Penyimpan<T> holds a value of type T and returns it as type T. An instance of Penyimpan<string> can only store and return strings, fully checked by the compiler.

Default Type Parameters

A type parameter can have a default value like a function parameter:

Type parameter default
interface Respons<T = string> {
    sukses: boolean;
    data: T;
}
 
const teksRespons: Respons = { sukses: true, data: "ok" };
const angkaRespons: Respons<number> = { sukses: true, data: 200 };

interface Respons<T = string> uses string when the caller doesn't mention the type. Calls with Respons<number> remain possible. Defaults make generic types easy to use for the common case without sacrificing flexibility for special cases.

Info

Type parameter naming convention: use a single uppercase letter like T, K, or V for simple cases, and descriptive names like Data or Error for generics whose meaning matters. Clear names help readers understand the relationships between types.

Closing

Episode 8 gives you the most powerful weapon of the TypeScript type system: generics. With functions, constraints, interfaces, classes, and default type parameters, you can write one implementation that's safe for many types without copying code or falling back to any.

Key takeaways:

  • Generics make one implementation work for many types.
  • TypeScript infers type parameters from call arguments.
  • The extends constraint limits a type parameter to a specific shape.
  • Interfaces and classes can also be parametric with type parameters.
  • Default type parameters make generics easy to use without type arguments.
  • Generics keep type guarantees intact throughout usage.

In the next episode 9 we'll discuss classes, inheritance, modifiers, and abstraction — how to build objects with shared behavior and control member access under the type system.