JV
TS

Java to TypeScript

10 lessons

Progress0%
1Introduction to TypeScript2Type Systems3Classes & OOP4Generics5Modules & Packages6Null Safety7Async vs Threads8Ecosystem9Advanced TypeScript Types10Build Tooling
All Mirror Courses
JV
TS
Generics
MirrorLesson 4 of 10
Lesson 4

Generics

Generics

Introduction

In this lesson, you'll learn about generics in TypeScript. Coming from Java, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
JV
From Java:

In Java, you're familiar with generics.

TS
In TypeScript:

TypeScript has its own approach to generics, which we'll explore step by step.

The TypeScript Way

Let's see how TypeScript handles this concept. Here's a typical example:

TS
TypeScript Example
// TypeScript generics - erased at compile time
class Box<T> {
    constructor(private value: T) {}
    getValue(): T { return this.value; }
}

// Constraint (like Java's upper-bounded wildcard)
function printNumbers<T extends number | bigint>(list: T[]): void {
    list.forEach(n => console.log(n));
}

// Conditional type (more powerful than wildcards)
type IsNumber<T> = T extends number ? true : false;

const strBox = new Box("hello");
const s: string = strBox.getValue();

Comparing to Java

Here's how you might have written similar code in Java:

JV
Java (What you know)
// Java generics - type erasure at runtime
class Box<T> {
    private T value;
    public Box(T value) { this.value = value; }
    public T getValue() { return value; }
}

// Bounded wildcard
void printNumbers(List<? extends Number> list) {
    for (Number n : list) {
        System.out.println(n.doubleValue());
    }
}

Box<String> strBox = new Box<>("hello");
String s = strBox.getValue();
Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Generic syntax is almost identical: Box<T>, <T extends Something>

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Java erases generic types at runtime (type erasure); TypeScript erases all types at compile time

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Java wildcards (? extends Number, ? super T) become TypeScript constraints (T extends number)

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

TypeScript has conditional types (T extends U ? X : Y) which are more powerful than Java wildcards

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

TypeScript allows more advanced type-level programming: mapped types, infer keyword, etc.

Step-by-Step Breakdown

1. Generic Functions

Generic functions work the same way — declare a type parameter before the parameter list.

JV
Java
public <T> T identity(T value) {
    return value;
}
TS
TypeScript
function identity<T>(value: T): T {
    return value;
}

// TypeScript infers the type argument:
const s = identity("hello"); // T = string
const n = identity(42);       // T = number

2. Generic Constraints

Use extends to constrain what types are allowed, just like Java's upper-bounded wildcards.

JV
Java
<T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}
TS
TypeScript
// Constrain T to types that have a length property
function longest<T extends { length: number }>(a: T, b: T): T {
    return a.length >= b.length ? a : b;
}

longest("alice", "bob");      // works — strings have length
longest([1, 2, 3], [4, 5]);   // works — arrays have length
Rule of Thumb
Use structural constraints ('T extends { length: number }') rather than named interfaces when the constraint is simple.

3. Conditional Types

TypeScript conditional types let you express type logic that has no equivalent in Java generics.

TS
TypeScript
// Unwrap a Promise type
type Awaited<T> = T extends Promise<infer U> ? U : T;

type A = Awaited<Promise<string>>;  // string
type B = Awaited<number>;           // number

// Make all properties optional
type Partial<T> = { [K in keyof T]?: T[K] };

interface User { name: string; age: number; }
type PartialUser = Partial<User>;
// { name?: string; age?: number; }

4. Generic Interfaces

TypeScript generic interfaces look and behave like Java generic interfaces.

JV
Java
interface Repository<T> {
    T findById(long id);
    List<T> findAll();
    void save(T entity);
}
TS
TypeScript
interface Repository<T> {
    findById(id: number): T | null;
    findAll(): T[];
    save(entity: T): void;
}

class UserRepository implements Repository<User> {
    findById(id: number): User | null { return null; }
    findAll(): User[] { return []; }
    save(user: User): void { }
}

Common Mistakes

When coming from Java, developers often make these mistakes:

  • Generic syntax is almost identical: Box<T>, <T extends Something>
  • Java erases generic types at runtime (type erasure); TypeScript erases all types at compile time
  • Java wildcards (? extends Number, ? super T) become TypeScript constraints (T extends number)
Common Pitfall
Don't assume TypeScript works exactly like Java. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Generic syntax <T> and constraints <T extends X> are nearly identical to Java
  • Both languages erase types (Java at runtime, TypeScript at compile time)
  • TypeScript conditional types replace Java wildcards and add more expressive power
  • Generic interfaces and classes translate almost line-for-line
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Java code in TypeScript to practice these concepts.
PreviousNext