TypeScript – Teil 2

Narrowing, fortgeschrittene Typen & Workshop

Informatik – 3. Semester

Software- und Web-Engineering I · TH Lübeck
1 Type Inference & Narrowing
2 Fortgeschrittene Typen
3 Mini-Workshop
4 Wrap-up & Ausblick

Type Inference und Type Narrowing

Software- und Web-Engineering I · TH Lübeck
Was bedeutet Typinferenz?
  • TypeScript leitet Typen aus Literalen, Ausdrücken und Rückgabewerten ab
  • Hilft, Boilerplate zu reduzieren, solange der Compiler genug Kontext hat
const count = 0;
const names = ['Ada'];
const pair = [1, 'x'];
Explizit typisieren, wenn Intent wichtig ist (z. B. const status: 'open')
Software- und Web-Engineering I · TH Lübeck
Grenzen der Inferenz
  • Bei any-Durchreichung geht Info verloren
  • let result; → any bis erste Zuweisung
  • Funktion ohne Rückgabetyp kann any ausschwemmen
function parse(data) {
  return JSON.parse(data);
}

const payload = parse('{}');
Software- und Web-Engineering I · TH Lübeck
Type Narrowing Basics
  • Ziel: Aus einer Union einen spezifischeren Typ ableiten
  • Strategien: typeof, instanceof, Equality Checks
function formatValue(value: string | number) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return value.toFixed(2);
}
Software- und Web-Engineering I · TH Lübeck
Das in-Operator Muster
  • Prüft, ob eine Eigenschaft auf einem Objekt existiert
type Success = { data: string };
type Failure = { error: string };
type Result = Success | Failure;

function handle(result: Result) {
  if ('data' in result) {
    console.log(result.data);
  } else {
    console.error(result.error);
  }
}
Software- und Web-Engineering I · TH Lübeck
instanceof & Klassenhierarchien
  • Funktioniert mit Klassen und Konstruktorfunktionen
class FileTracker {
  constructor(public path: string) {}
}

function log(item: FileTracker | string) {
  if (item instanceof FileTracker) {
    console.log('tracking', item.path);
  } else {
    console.log('raw path', item);
  }
}
Software- und Web-Engineering I · TH Lübeck
Benutzerdefinierte Type Guards
  • Eigene Funktionen, die eine Type-Predicate-Signatur haben
type TeamMember = { name: string; skills?: string[] };

function hasSkills(member: TeamMember): member is TeamMember & { skills: string[] } {
  return Array.isArray(member.skills) && member.skills.length > 0;
}

function describe(member: TeamMember) {
  if (hasSkills(member)) {
    return `${member.name}: ${member.skills.join(', ')}`;
  }
  return `${member.name}: Skills TBD`;
}
Software- und Web-Engineering I · TH Lübeck
Typinferenz steuern
  • as const konserviert Literaltypen
  • Generics mit Default (<T = string>) erleichtern APIs
  • Enums/const Assertions halten Werte stabil
const config = {
  env: 'prod',
  retries: 3,
} as const;

type Config = typeof config;
Software- und Web-Engineering I · TH Lübeck
Utility-Funktion absichern
type User = { id: number; name: string };
type Team = User | User[];

function getFirst(entity: Team) {
  // TODO: Type Guards ergänzen
}
  • Ergänzt Narrowing, sodass getFirst immer einen User zurückgibt
  • Schritt 1: Prüfen auf Array Array.isArray
  • Schritt 2: Optional benutzerdefinierten Guard schreiben
5 min
Software- und Web-Engineering I · TH Lübeck
function getFirst(entity: Team): User {
  if (Array.isArray(entity)) {
    return entity[0];
  }
  return entity;
}
Software- und Web-Engineering I · TH Lübeck
1 Type Inference & Narrowing
2 Fortgeschrittene Typen
3 Mini-Workshop
4 Wrap-up & Ausblick

die wichtigsten TS Konstrukte

Software- und Web-Engineering I · TH Lübeck
Literal Types & konstante Werte
  • Literaltypen beschränken Variablen auf konkrete Werte
  • Oft genutzt für Status, Aktionen, Feature-Flags
type Theme = 'light' | 'dark';
const currentTheme: Theme = 'light';

function setTheme(theme: Theme) {
  document.body.dataset.theme = theme;
}
Software- und Web-Engineering I · TH Lübeck
Discriminated Unions
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape) {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'rectangle':
      return shape.width * shape.height;
  }
}
Software- und Web-Engineering I · TH Lübeck
Mapped Types
  • Typen über vorhandene Keys generieren
type Optional<T> = {
  [K in keyof T]?: T[K];
};

type User = { id: number; name: string; email: string };
type UserDraft = Optional<User>;
Software- und Web-Engineering I · TH Lübeck
Utility Types im Alltag
  • Partial<T> – macht alle Felder optional
  • Pick<T, K> – wählt Felder aus
  • Record<K, T> – Map von Keys zu Werten
  • Readonly<T> – verhindert Mutation
type User = { id: number; name: string; email: string };
type UpdateUser = Partial<User>;
type UserPreview = Pick<User, 'id' | 'name'>;
type Permissions = Record<'read' | 'write', boolean>;
Software- und Web-Engineering I · TH Lübeck
Conditional Types
  • Typentscheidung basierend auf einer Bedingung extends ? A : B
type ResponseType<T> = T extends Promise<infer U> ? U : T;

type A = ResponseType<Promise<string>>;
type B = ResponseType<number>;
Software- und Web-Engineering I · TH Lübeck
any vs. unknown
  • any: Deaktiviert Typprüfung – schnelle, aber unsichere Lösung
  • unknown: Typ muss vor Nutzung eingegrenzt werden
function handleInput(value: unknown) {
  if (typeof value === 'string') {
    return value.trim();
  }
  throw new Error('Unsupported type');
}
Software- und Web-Engineering I · TH Lübeck
Mini-Quiz
  • TypeScript hat globale Utility Types, die auf Typen angewandt werden können
  • Discriminated Unions sind perfekt, um einen Typen zu “narrown”
  1. Welcher Utility Type eignet sich für eine Map von Rollen zu Rechten?
  2. Wie signalisiert ihr bei Discriminated Unions den Typ?
  3. Welcher Conditional Type entpackt Promise<T>?
Software- und Web-Engineering I · TH Lübeck
1 Type Inference & Narrowing
2 Fortgeschrittene Typen
3 Mini-Workshop
4 Wrap-up & Ausblick

Modul strikt typisieren

Software- und Web-Engineering I · TH Lübeck
Zielsetzung des Workshops
  • Kleines Modul (Task-Board) gemeinsam von any zu sauberen Typen führen
  • Fokus: Datenmodell, Service-Funktionen, Fehlerobjekte
  • Output: Pull-Request-ready Typdefinition + kurze Tests
Software- und Web-Engineering I · TH Lübeck
Ausgangscode (vereinfachter Zustand)
// board.js
let tasks = [];

function addTask(task) {
  tasks.push(task);
}

function findTask(id) {
  return tasks.find(t => t.id === id);
}
Software- und Web-Engineering I · TH Lübeck
Ergebnis
type Task = { id: string; title: string; status: 'open' | 'done'; assignee?: string };
type Board = { tasks: Task[] };

function addTask(board: Board, task: Task): Board {
  return { ...board, tasks: [...board.tasks, task] };
}

function findTask(board: Board, id: string): Task | undefined {
  return board.tasks.find(task => task.id === id);
}
Software- und Web-Engineering I · TH Lübeck
1 Type Inference & Narrowing
2 Fortgeschrittene Typen
3 Mini-Workshop
4 Wrap-up & Ausblick

Wie geht’s weiter?

Software- und Web-Engineering I · TH Lübeck
Wichtigste Takeaways
  • Typsystem = Team-Superpower: Fehlertypen rutschen vom Prod-Log ins IDE-Panel
  • Kombination aus Inferenz + gezielten Annotationen liefert Tempo und Sicherheit
  • Generics & Utility Types helfen, Code zu teilen statt zu kopieren
Software- und Web-Engineering I · TH Lübeck
Was nehmt ihr mit?
  • Welches Konzept wollt ihr sofort im Projekt einsetzen?
  • Wo braucht ihr noch Support?
2 min
Software- und Web-Engineering I · TH Lübeck

Q&A

+ Feedbackrunde

Software- und Web-Engineering I · TH Lübeck

- Einstieg: zweiter Teil zu TypeScript, heute Inference, Narrowing und die fortgeschrittenen Konstrukte - Nicht auf dem Titel verweilen

- Kurz an Teil 1 anknuepfen: Typsystem-Grundlagen sitzen, jetzt die Compiler-Intelligenz - Roadmap zeigen: vier Stationen, am Ende der Workshop

- Typen im Beispiel erfragen: number, string[], (number | string)[] - Faustregel: Inferenz nutzen, bei oeffentlichen APIs explizit typisieren

- JSON.parse liefert any, payload ist damit ungeschuetzt - Empfehlung: Rueckgabewerte bei oeffentlichen APIs annotieren

- Im if-Zweig ist value string, danach automatisch number - Der Editor versteht die Pfade ohne Zusatzaufwand

- 'data' in result unterscheidet die beiden Formen der Union - Rueckbezug zu Teil 1: genau unser Result-Typ aus der Uebung

- instanceof prueft die Prototype-Kette zur Laufzeit - Hinweis: fuer reine Interface-Typen nicht nutzbar, die existieren zur Laufzeit nicht

- "member is ..." ist die Predicate-Signatur, sie macht das Narrowing wiederverwendbar - Nach dem if kennt der Compiler skills als string[]

- as const: env wird 'prod' statt string, alles readonly - typeof config macht daraus einen praezisen Typ ohne Doppelpflege

- Pair Programming, danach kurze Loesung im Plenum - Hinweis auf Array.isArray als eingebauten Guard geben

- Musterloesung: Array.isArray narrowt auf User[] bzw. User - Optional diskutieren: was passiert bei leerem Array?

- Jetzt die Konstrukte, die in echten Codebasen staendig auftauchen

- Nur 'light' oder 'dark' sind erlaubt, Tippfehler fallen sofort auf - Typische Einsatzfaelle: Status, Aktionen, Flags

- Gemeinsames kind-Feld als Diskriminator, switch narrowt pro Fall - Muster fuer API-Antworten, Events, Zustandsmodelle empfehlen

- [K in keyof T] iteriert ueber alle Keys, ? macht sie optional - Praktisch fuer DTOs, Formulare, Partial-Updates

- Utility Types sind eingebaute Mapped Types - Kurz fragen: welche Utility Types nutzt ihr bereits?

- infer U entpackt den Promise-Inhalt: A = string, B = number - Nur als Werkzeug vorstellen, nicht in die Tiefe gehen

- unknown zwingt zum Narrowing vor der Nutzung, any nicht - Frage ans Plenum: wann ist any vertretbar?

- Erwartete Antworten: 1. Record, 2. gemeinsames kind-Feld, 3. infer in Conditional Type - Fragen nacheinander stellen, kurz abstimmen lassen

- Jetzt anwenden: ein kleines Modul gemeinsam von any zu sauberen Typen

- Bezug zum Semesterprojekt: Task-Board ist bewusst projektnah gewaehlt - Erwartungsmanagement: kleine Schritte, gemeinsames Refactoring

- Probleme benennen lassen: alles any, keine Fehlerbehandlung, globaler Zustand - Teams ueberlegen zuerst das Datenmodell (Task, Board)

- Datenmodell mit Literal-Union fuer status, optionaler assignee - Funktionen pure gemacht: Board rein, neues Board raus - Task | undefined zwingt Aufrufer zum Narrowing

- Abschluss: Takeaways sichern und Bogen zu den naechsten Vorlesungen

- Kernaussagen langsam und deutlich formulieren - Studierende in eigenen Worten wiederholen lassen

- Think-Pair-Share, danach Support-Wuensche einsammeln - Wuensche fuer kommende Uebungen notieren

- Offene Fragen zu Stoff, Uebungen oder Projektumsetzung - Reminder: Slides und Code-Snippets stehen im Repo bereit