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
Advanced TypeScript Types
MirrorLesson 9 of 10
Lesson 9

Advanced TypeScript Types

Mapped types, conditional types, template literals — TypeScript type magic

Introduction

In this lesson, you'll learn about advanced typescript types 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 mapped types, conditional types, template literals — typescript type magic.

TS
In TypeScript:

TypeScript has its own approach to mapped types, conditional types, template literals — typescript type magic, 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
// Mapped types — transform all properties of T
type Partial<T>  = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// Conditional types
type NonNullable<T> = T extends null | undefined ? never : T;
type ReturnType<F>  = F extends (...args: any[]) => infer R ? R : never;

// Utility types (built-in)
type UpdateUserDto = Partial<User>;  // all props optional
type UserKeys      = keyof User;     // "name" | "email" | "age"
type PickedUser    = Pick<User, "name" | "email">;
type OmitId        = Omit<User, "id">;

// Template literal types
type EventName = "click" | "focus";
type Handler   = `on${Capitalize<EventName>}`; // "onClick"|"onFocus"

// Discriminated unions (like sealed classes)
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect";   w: number; h: number };

function area(s: Shape): number {
  return s.kind === "circle"
    ? Math.PI * s.radius ** 2
    : s.w * s.h;
}

// satisfies operator
const config = {
  port: 8080, debug: true
} satisfies Record<string, number | boolean>;
// config.port is still number (not widened to number|boolean)

Comparing to Java

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

JV
Java (What you know)
// Java: generics with bounds
public <T extends Comparable<T>> T max(List<T> list) {
    return Collections.max(list);
}

// Wildcard types
void printAll(List<? extends Number> list) {
    list.forEach(System.out::println);
}

// No mapped types — each variant needs a new class
public class UpdateUserDto {
    private String name;  // optional
    private String email; // optional
    // Java: explicit optional fields or @Nullable
}

// Records for immutable data
record Point(double x, double y) {}

// Sealed hierarchy
sealed interface Shape permits Circle, Rect {}
record Circle(double r)   implements Shape {}
record Rect(double w, double h) implements Shape {}
Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Mapped types ([K in keyof T]) transform entire object types — no Java equivalent

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Built-in utility types: Partial<T>, Required, Readonly, Pick, Omit, Record

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Conditional types (T extends X ? A : B) work at compile time — not runtime

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Template literal types create string patterns like `on${Capitalize<E>}`

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

TS
In TypeScript:

Discriminated unions (kind field) replaces sealed classes for type-safe branching

Step-by-Step Breakdown

1. Utility Types

TypeScript's built-in utility types transform existing types. Partial<T> makes all fields optional — common for update DTOs.

JV
Java
// Java: create UpdateDto manually with nullable fields
record UpdateDto(String name, String email) {}
TS
TypeScript
type UpdateDto = Partial<User>;  // all fields optional
type ReadonlyUser = Readonly<User>;
type NameAndEmail = Pick<User, "name" | "email">;
type NoId = Omit<User, "id">;

2. Mapped Types

Build custom transformations with [K in keyof T]. This is how all the built-in utility types are implemented.

JV
Java
// Java: no equivalent — each variant is a new class
TS
TypeScript
// Make all props nullable
type Nullable<T> = { [K in keyof T]: T[K] | null };
// Add prefix to keys
type Prefixed<T> = { [`get_${string & keyof T}`]: T[keyof T] };

3. Conditional Types

T extends X ? A : B is evaluated at compile time. Useful for extracting types from function signatures or array elements.

JV
Java
// Java: Class<T> getReturnType(Method m) — runtime reflection
TS
TypeScript
// Extract return type at compile time
type ReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type R = ReturnType<typeof fetch>; // Promise<Response>

4. Discriminated Unions

A literal 'kind' field makes TypeScript narrow the type inside an if/switch — equivalent to Java's sealed class + pattern matching.

JV
Java
sealed interface Shape permits Circle, Rect {}
switch(shape) { case Circle c -> c.r(); }
TS
TypeScript
type Shape = { kind:"circle"; r:number } | { kind:"rect"; w:number; h:number };
if (s.kind === "circle") {
    s.r; // TypeScript knows it's circle here
}

Common Mistakes

When coming from Java, developers often make these mistakes:

  • Mapped types ([K in keyof T]) transform entire object types — no Java equivalent
  • Built-in utility types: Partial<T>, Required, Readonly, Pick, Omit, Record
  • Conditional types (T extends X ? A : B) work at compile time — not runtime
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

  • Utility types: Partial<T>, Required, Readonly, Pick, Omit — transform types without new class declarations
  • Mapped types ([K in keyof T]) build custom transforms — basis for all utility types
  • Conditional types (T extends X ? A : B) work at compile time with 'infer' for extraction
  • Discriminated unions + literal 'kind' field = TypeScript's sealed class + pattern matching
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