Generic Interfaces, Aliases & Classes
Intermediate · 21 min read · ▶ live playground · ✦ checkpoint
Generic functions are only the first place type parameters show up. Once you can read "for some type T," the same idea applies to reusable object shapes, data structures, and classes: the caller chooses the type once, and the whole shape stays consistent.
Generic aliases: reusable wrappers
A type alias can take type parameters the same way a function can. The alias does not create a value at runtime; it creates a reusable type pattern.
Two patterns show up constantly in application code:
Result<T>: an operation either succeeds with a value of T or fails with a message.Paginated<T>: a page contains items of T plus page metadata.
Read Result<T> out loud: "Result, for some type T, is either an ok value of T or an error message." The alias stores the relationship once, then every use fills in the concrete type: Result<LessonSummary>, Result<string>, Result<number[]>, and so on.
Paginated<T> does the same thing for containers. The metadata is always the same, but items changes with the caller's type. That is the alias version of link input type to output type: one chosen T flows through every field that mentions it.
Generic interfaces: a typed Stack
Interfaces can take type parameters too. This is useful when you want to describe a contract first, then let a class or object implement it.
Here is a small stack: push items on top, peek at the top, pop the top off. The interface says the stack is consistent for one item type.
The type argument on new ArrayStack<BuildStep>() matters because an empty stack has no first item for inference to inspect. After that, the class enforces the same T everywhere: push accepts BuildStep, pop returns BuildStep | undefined, and peek returns BuildStep | undefined.
This is also why the interface is valuable. Code can depend on Stack<T> without caring whether the implementation uses an array, a linked list, or something else. The data structure contract is generic; the storage choice is ordinary JavaScript.
Generic classes have runtime limits
A generic class is still one JavaScript class at runtime. ArrayStack<string> and ArrayStack<BuildStep> are checked differently by TypeScript, but they do not become two different constructors after compilation. The type parameter is for checking values that enter and leave the class; it is not a runtime value the class can call.
class Maker<T> {
create(): T {
return new T();
}
}→ playground.ts:3:16 - error TS2693: 'T' only refers to a type, but is being used as a value here.
The fix is not a special generic trick. If a class needs to create something at runtime, pass in a runtime value it can actually call, or pass in ready-made values and let the generic type check how they move through the class. Section 13 comes back to this boundary from the data-validation side; for now, keep the rule simple: type parameters erase.
Reading the standard library
You have been using generic types from the standard library since arrays and promises first appeared. The real declarations are much larger, but the important relationships read like this:
type ArrayReading<T> = {
length: number;
at(index: number): T | undefined;
map<U>(callback: (item: T) => U): U[];
};
type PromiseReading<T> = {
then<U>(callback: (value: T) => U): PromiseReading<U>;
};
type MapReading<K, V> = {
get(key: K): V | undefined;
set(key: K, value: V): MapReading<K, V>;
};Array<T> means every element is a T. Promise<T> means the not-yet value resolves to a T. Map<K, V> has two type parameters: K for keys, V for values. When you read library types, do not try to memorize every method. Find the type parameters, then follow where each one appears.
Notice the return types. names.map(...) introduces a second type parameter for the callback result, so mapping strings to uppercase strings gives string[]. scores.get("quiz") returns number | undefined, because the key type can be correct and the key can still be missing. Generics preserve type relationships; they do not promise that runtime data exists.
Next lesson adds defaults and newer inference controls. The same reading habit will carry you through it: name the type parameters, ask where they are solved, then follow the relationship.
Checkpoint
Answer all three to mark this lesson complete