Die diskriminierten Unions von TypeScript sind eine leistungsstarke Funktion, die den Mustervergleich auf die nächste Ebene hebt. Sie ermöglichen es uns, komplexe, typsichere bedingte Logik zu erstellen, die über einfache Switch-Anweisungen hinausgeht. Ich habe diese Technik in meinen letzten Projekten ausgiebig genutzt und sie hat meine Herangehensweise an den Kontrollfluss in TypeScript verändert.
Beginnen wir mit den Grundlagen. Eine diskriminierte Union ist ein Typ, der eine gemeinsame Eigenschaft verwendet, um zwischen verschiedenen Varianten zu unterscheiden. Hier ist ein einfaches Beispiel:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number }
Die Eigenschaft „freundlich“ ist hier unser Unterscheidungsmerkmal. Dadurch kann TypeScript anhand seines Werts ableiten, mit welcher spezifischen Form wir es zu tun haben.
Jetzt wollen wir sehen, wie wir dies für den Mustervergleich verwenden können:
function getArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height } }
Das ist nett, aber es ist erst der Anfang. Wir können noch viel weiter gehen.
Einer der wirkungsvollsten Aspekte diskriminierter Gewerkschaften ist die Vollständigkeitsprüfung. TypeScript kann sicherstellen, dass wir bei unserem Mustervergleich alle möglichen Fälle behandelt haben. Fügen wir unserer Gewerkschaft eine neue Form hinzu:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number } | { kind: 'triangle'; base: number; height: number } function getArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height // TypeScript will now warn us that we're not handling the 'triangle' case } }
Um dies noch robuster zu machen, können wir einen Standardfall hinzufügen, der einen Fehler auslöst, um sicherzustellen, dass wir nie versehentlich vergessen, einen neuen Fall zu bearbeiten:
function assertNever(x: never): never { throw new Error("Unexpected object: " + x); } function getArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height case 'triangle': return 0.5 * shape.base * shape.height default: return assertNever(shape) } }
Wenn wir nun jemals eine neue Form hinzufügen, ohne unsere getArea-Funktion zu aktualisieren, gibt uns TypeScript einen Fehler bei der Kompilierung.
Aber wir können mit dem Mustervergleich noch weiter gehen. Schauen wir uns ein komplexeres Beispiel mit verschachtelten Mustern an.
Stellen Sie sich vor, wir bauen eine einfache Zustandsmaschine für eine Ampel:
type TrafficLightState = | { state: 'green' } | { state: 'yellow' } | { state: 'red' } | { state: 'flashing', color: 'yellow' | 'red' } function getNextState(current: TrafficLightState): TrafficLightState { switch (current.state) { case 'green': return { state: 'yellow' } case 'yellow': return { state: 'red' } case 'red': return { state: 'green' } case 'flashing': return current.color === 'yellow' ? { state: 'red' } : { state: 'flashing', color: 'yellow' } } }
Hier führen wir nicht nur einen Abgleich auf der obersten Ebene durch, sondern auch auf verschachtelten Eigenschaften, wenn wir uns im Status „Blinken“ befinden.
Wir können Guards auch verwenden, um unserem Mustervergleich noch komplexere Bedingungen hinzuzufügen:
type WeatherEvent = | { kind: 'temperature', celsius: number } | { kind: 'wind', speed: number } | { kind: 'precipitation', amount: number } function describeWeather(event: WeatherEvent): string { switch (event.kind) { case 'temperature': if (event.celsius > 30) return "It's hot!" if (event.celsius < 0) return "It's freezing!" return "The temperature is moderate." case 'wind': if (event.speed > 100) return "There's a hurricane!" if (event.speed > 50) return "It's very windy." return "There's a gentle breeze." case 'precipitation': if (event.amount > 100) return "It's pouring!" if (event.amount > 0) return "It's raining." return "It's dry." } }
Dieser Mustervergleichsansatz ist nicht auf Switch-Anweisungen beschränkt. Wir können es mit if-else-Ketten oder sogar mit Objektliteralen für komplexere Szenarien verwenden:
type Action = | { type: 'INCREMENT' } | { type: 'DECREMENT' } | { type: 'RESET' } | { type: 'SET', payload: number } const reducer = (state: number, action: Action): number => ({ INCREMENT: () => state + 1, DECREMENT: () => state - 1, RESET: () => 0, SET: () => action.payload, }[action.type]())
Dieser Ansatz kann besonders nützlich sein, wenn das Besuchermuster implementiert wird. Hier ist ein Beispiel dafür, wie wir diskriminierte Gewerkschaften verwenden könnten, um einen einfachen Ausdrucksauswerter zu implementieren:
type Expr = | { kind: 'number'; value: number } | { kind: 'add'; left: Expr; right: Expr } | { kind: 'multiply'; left: Expr; right: Expr } const evaluate = (expr: Expr): number => { switch (expr.kind) { case 'number': return expr.value case 'add': return evaluate(expr.left) + evaluate(expr.right) case 'multiply': return evaluate(expr.left) * evaluate(expr.right) } } const expr: Expr = { kind: 'add', left: { kind: 'number', value: 5 }, right: { kind: 'multiply', left: { kind: 'number', value: 3 }, right: { kind: 'number', value: 7 } } } console.log(evaluate(expr)) // Outputs: 26
Mit diesem Muster können wir unser Ausdruckssystem problemlos um neue Ausdruckstypen erweitern, und TypeScript stellt sicher, dass wir alle Fälle in unserer Auswertungsfunktion verarbeiten.
Einer der wirkungsvollsten Aspekte dieses Ansatzes besteht darin, dass er es uns ermöglicht, große, komplexe bedingte Blöcke in besser verwaltbare und erweiterbare Strukturen umzuwandeln. Schauen wir uns ein komplexeres Beispiel an:
Stellen Sie sich vor, wir bauen ein System zur Verarbeitung verschiedener Arten von Finanztransaktionen auf:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number }
In diesem Beispiel haben wir die zugeordneten Typen und bedingten Typen von TypeScript verwendet, um ein typsicheres Objekt zu erstellen, bei dem jeder Schlüssel einem Transaktionstyp entspricht und jeder Wert eine Funktion ist, die diesen spezifischen Transaktionstyp verarbeitet. Dieser Ansatz ermöglicht es uns, problemlos neue Arten von Transaktionen hinzuzufügen, ohne die Kernlogik unserer handleTransaction-Funktion zu ändern.
Das Schöne an diesem Muster ist, dass es sowohl typsicher als auch erweiterbar ist. Wenn wir einen neuen Transaktionstyp hinzufügen, zwingt uns TypeScript, eine entsprechende Prozessorfunktion hinzuzufügen. Wenn wir versuchen, eine nicht vorhandene Transaktionsart zu verarbeiten, erhalten wir einen Fehler bei der Kompilierung.
Dieser Mustervergleichsansatz mit diskriminierten Unions kann zu aussagekräftigerem, sichererem und selbstdokumentierenderem TypeScript-Code führen, insbesondere in komplexen Anwendungen. Es ermöglicht uns, komplexe Logik auf eine Weise zu handhaben, die sowohl lesbar als auch wartbar ist.
Da unsere Anwendungen immer komplexer werden, werden diese Techniken immer wertvoller. Sie ermöglichen es uns, Code zu schreiben, der nicht nur korrekt, sondern auch leicht zu verstehen und zu ändern ist. Indem wir das Typsystem von TypeScript optimal nutzen, können wir robuste, flexible Systeme erstellen, mit denen die Arbeit Freude macht.
Denken Sie daran, dass das Ziel nicht nur darin besteht, funktionierenden Code zu schreiben, sondern auch darin, Code zu schreiben, der seine Absicht klar zum Ausdruck bringt und fehlerresistent ist, wenn sich die Anforderungen ändern. Der Musterabgleich mit diskriminierten Gewerkschaften ist ein wirksames Instrument zur Erreichung dieses Ziels.
Meiner Erfahrung nach hat die Übernahme dieser Muster zu erheblichen Verbesserungen der Codequalität und Entwicklungsgeschwindigkeit geführt. Es dauert einige Zeit, sich daran zu gewöhnen, in den Begriffen diskriminierter Vereinigungen und umfassender Mustervergleiche zu denken, aber wenn Sie dies einmal getan haben, werden Sie feststellen, dass es neue Möglichkeiten für die Strukturierung Ihres Codes auf klare, typsichere Weise eröffnet.
Wenn Sie TypeScript weiter erkunden, empfehle ich Ihnen, nach Möglichkeiten zu suchen, diese Muster in Ihrem eigenen Code anzuwenden. Fangen Sie klein an, vielleicht indem Sie eine komplexe If-Else-Kette in eine diskriminierte Union umgestalten. Wenn Sie mit der Technik vertrauter werden, werden Sie immer mehr Stellen sehen, an denen sie angewendet werden kann, um Ihren Code zu vereinfachen und zu verdeutlichen.
Denken Sie daran, dass die wahre Stärke von TypeScript nicht nur in seiner Fähigkeit liegt, Fehler zu erkennen, sondern auch in seiner Fähigkeit, uns zu besseren, ausdrucksstärkeren Codestrukturen zu führen. Indem wir Muster wie diskriminierte Gewerkschaften und umfassenden Mustervergleich berücksichtigen, können wir Code erstellen, der nicht nur korrekt ist, sondern auch Spaß beim Lesen und Verwalten macht.
Schauen Sie sich unbedingt unsere Kreationen an:
Investor Central | Intelligentes Leben | Epochen & Echos | Rätselhafte Geheimnisse | Hindutva | Elite-Entwickler | JS-Schulen
Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Wissenschaft & Epochen Medium | Modernes Hindutva
Das obige ist der detaillierte Inhalt vonBeherrschen Sie den Pattern Matching von TypeScript: Steigern Sie die Leistung und Sicherheit Ihres Codes. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!