
Warum flatMap() einfacher als filter() in TypeScript-Apps ist

Irena
14. Juli 2024
Hinweis: Dieser Artikel wurde unter Zuhilfenahme von KI aus dem Englischen übersetzt. Hier geht's zum Originaltext.
Bevor wir in die Programmier-Bubble eintauchen, hier ein kleiner Exkurs für neugierige Gemüter: Du fragst dich, was es mit der Katze im Bild auf sich hat? Das ist die süße Bärbel! 🐈 Verwirrt vom neongrünen Hintergrund? Das ist eine Anspielung auf die Internetkultur! Man lernt nie aus!
Nimm dir kurz Zeit mal einen Moment & frag dich: "Was ist meine liebste Array-Methode?"
Als TypeScript/React-Entwicklerin liebe ich eine gute alte map-Funktion. Es hat etwas seltsam Befriedigendes, mit .map() über Elemente zu iterieren und JSX-Elemente zu erstellen. Seit einiger Zeit nutze ich allerdings intensiv flatMap() in meinem TypeScript-Code. Lass uns ergründen, warum das so ist und wie Neuerungen in TypeScript 5.5 das wieder ändern könnten!
Für alle, die auf meine Anfangsfrage mit 'reduce' geantwortet haben: Na komm schon!
Stell dir vor, du hast ein Array von Elementen. Du möchtest jedes Element transformieren und gleichzeitig einige basierend auf bestimmten Bedingungen herausfiltern. Traditionell würdest du vielleicht .map() und .filter() verketten. Das habe ich auch gemacht, bis mein Kollege Johannes mich darauf hinwies, dass ich flatMap verwenden könnte. Das hat mich neugierig gemacht, ich hab mich reingestürzt und will jetzt mit dir teilen, was dabei rausgekommen ist. Lass uns meine zwei Lieblingsthemen kombinieren: TypeScript und Katzen 🐈⬛.
Wir beginnen mit einem Beispiel, das map() und filter() verwendet, und das wir dann zu flatMap() umbauen werden.
Verwendung von map() und filter()
Im obigen Code-Snippet haben wir ein Array von Katzen-Objekten. Wir filtern und mappen unsere Katzen nach Chonk-Faktor, um eine Liste der moppeligsten Exemplare zu erhalten.
Verwendung von flatMap()
In diesem Code-Snippet verwenden wir flatMap(), um das Array von Katzen direkt in einem Durchgang zu filtern und in ein Array von Namen umzuwandeln. Wenn die Bedingung erfüllt ist, geben wir ein Array zurück, das den Namen enthält; andernfalls geben wir ein leeres Array zurück. Das Ergebnis ist dasselbe wie mit map() und filter() erreicht, aber der Teufel steckt im Detail. Wenn wir jetzt einen genaueren Blick auf unseren Namenslistentyp werfen, glänzt flatMap richtig.
Wie du vielleicht bemerkt hast, gehört zu unserer Katzenbande auch eine namenlose, abgemagerte Streunerkatze (armer Kerl). Da wir den Namen nicht kennen, filtern wir in beiden Snippets undefinierte Namen heraus. Haben unsere moppeligen Namenslisten beide den gleichen Typ? Spoiler: Nein.
Der Grund dafür ist, dass TypeScript diese Operationen unabhängig behandelt. Zuerst erstellt filter ein neues Array, das Elemente enthält, die die angegebene Bedingung erfüllen. Das Typsystem von TypeScript verfeinert jedoch nicht den Typ der Elemente basierend auf Laufzeitprüfungen (wie cat.name != null im Beispiel). Daher bleibt der Typ der Array-Elemente nach dem Filtern derselbe wie beim ursprünglichen Array, einschließlich der Möglichkeit von undefinierten Werten, wenn die Array-Elemente optional oder nullbar waren.
Wenn dann map angewendet wird, transformiert es jedes Element in eine neue Form (in diesem Fall wird die Eigenschaft 'name' extrahiert). Da das ursprüngliche Array Elemente mit undefinierten Namen enthalten könnte, wird das resultierende Array aus map als (string | undefined)[] typisiert, was darauf hinweist, dass einige Elemente möglicherweise nicht transformiert wurden, weil sie von Anfang an undefiniert waren.
flatMap hingegen kombiniert Filtern und Mappen in einem einzigen Schritt. Wenn du ein Array aus dem Callback zurückgibst, der an flatMap übergeben wird (wie das Zurückgeben von cat.name in einem Array, wenn es existiert, oder eines leeren Arrays, wenn nicht), versteht TypeScript, dass du absichtlich bestimmte Arten von Elementen ausschließt (in diesem Fall undefinierte Namen). Das liegt daran, dass das leere Array ([]) das Fehlen eines Elements für Fälle anzeigt, die die Bedingung nicht erfüllen, und sie somit effektiv herausfiltert.
Daher ermöglicht flatMap TypeScript, einen präziseren Typ für das resultierende Array abzuleiten. Da alle Elemente, die potenziell undefiniert sein könnten, durch leere Arrays ersetzt werden (und somit herausgefiltert werden), wird das resultierende Array aus flatMap als string[] typisiert, was darauf hinweist, dass es nur Strings enthält. Dies ist eine genauere Darstellung des Laufzeitverhaltens, bei dem nur definierte Namen im endgültigen Array enthalten sind.
Bonus TypeScript 5.5 Fun
TypeScript 5.5 hat die Beziehung zwischen .filter und TypeScript verbessert, wenn wir den Filter in der richtigen Reihenfolge anwenden:
Gut zu wissen: Du fragst dich vielleicht, warum der filter nach der map-Funktion kommen muss. Zum Zeitpunkt des Schreibens scheint TypeScript die beschriebene Typverfeinerung nur für einfache Fälle wie typeof name != null vorzunehmen, aber nicht für komplexere Fälle, bei denen der gesamte Objekttyp verfeinert werden muss. Das Filtern zuerst wird uns (noch) nicht das erwartete Ergebnis liefern.
In TypeScript 5.4 und darunter wäre der Typ immer noch (string | undefined)[], da das Typprädikat nicht abgeleitet werden konnte.
Aber lass uns nicht nur auf mein Wort vertrauen. Probier es selbst aus! Schau dir das Beispiel im TypeScript Playground an und sieh, wie sich die abgeleiteten Typen in früheren TS-Versionen verhalten 💡
🤖 Statement zur Verwendung von KI in diesem Artikel: Dieser Artikel wurde von Menschen geschrieben (Danke für das Feedback Leo, Bene & Johannes!), einschließlich Titel, Konzepte und Code-Beispiele. Allerdings haben wir KI verwendet, um den Schreibstil zu verbessern.
Typescript 5.5
Array Methods
flatMap
filter
map
Weitere Themen

Michael, 16.04.2025
Green Hosting im Nachhaltigkeitsvergleich
Sustainability
Green Software
Consulting
Hostingsec

Lea, 04.04.2025
Vue clever nutzen – Wiederverwendbarkeit für Einsteiger:innen
Vue
JavaScript
Reusability
DRY Principle
Components
Composables

Francesca, Ricarda, 02.04.2025
Die 10 häufigsten Fehler bei der Entwicklung digitaler Produkte – und wie du sie vermeidest
MVP development
UX/UI design
product vision
agile process
user engagement
product development