


So verwenden Sie TypeScript zum Akkumulieren von Typen: Eingabe ALLER möglichen fetch()-Ergebnisse
Als ich (mit meinem Team) anfing, unsere Anwendung in TypeScript und Svelte neu zu schreiben (es war in JavaScript und React, was wir alle hassen), stand ich vor einem Problem:
Wie kann ich alle möglichen Textkörper einer HTTP-Antwort sicher eingeben?
Kommt Ihnen das bekannt? Wenn nicht, bist du höchstwahrscheinlich „einer von denen“, hehe. Lassen Sie uns einen Moment abschweifen, um das Bild besser zu verstehen.
Warum dieses Gebiet unerforscht zu sein scheint
Niemand scheint sich für „alle möglichen Körper“ einer HTTP-Antwort zu interessieren, da ich nichts finden konnte, was bereits dafür gemacht wurde (naja, vielleicht ts-fetch). Lassen Sie mich hier kurz meine Logik durchgehen, warum das so ist.
Niemand kümmert sich darum, weil die Leute auch:
Kümmere dich nur um den glücklichen Weg: Der Antworttext, wenn der HTTP-Statuscode 2xx ist.
Die Leute geben es manuell woanders ein.
Zu Punkt 1 würde ich sagen: Ja, Entwickler (insbesondere die unerfahrenen) vergessen, dass eine HTTP-Anfrage fehlschlagen kann und dass die in der fehlgeschlagenen Antwort enthaltenen Informationen höchstwahrscheinlich völlig anders sind als die reguläre Antwort.
Für #2 wollen wir uns mit einem großen Problem befassen, das in beliebten NPM-Paketen wie ky und axios auftritt.
Das Problem beim Datenabruf von Paketen
Soweit ich das beurteilen kann, mögen Leute Pakete wie ky oder axios, weil eines ihrer „Features“ darin besteht, dass sie bei nicht-OK-HTTP-Statuscodes einen Fehler auslösen. Seit wann ist das in Ordnung? Da noch nie. Aber offenbar greifen die Leute das nicht auf. Die Leute sind glücklich und zufrieden, wenn sie Fehler bei nicht OK-Antworten erhalten.
Ich stelle mir vor, dass Leute nicht-OK-Körper eingeben, wenn es Zeit zum Fangen ist. Was für ein Durcheinander, was für ein Code-Geruch!
Das ist ein Codegeruch, weil Sie try..catch-Blöcke effektiv als Verzweigungsanweisungen verwenden und try..catch nicht als Verzweigungsanweisung gedacht ist.
Aber selbst wenn Sie mit mir argumentieren würden, dass die Verzweigung in try..catch auf natürliche Weise erfolgt, gibt es einen weiteren wichtigen Grund, warum dies weiterhin schlecht ist: Wenn ein Fehler ausgegeben wird, muss die Laufzeit den Aufrufstapel abwickeln. Dies ist im Hinblick auf die CPU-Zyklen weitaus kostspieliger als eine normale Verzweigung mit einer if- oder switch-Anweisung.
Können Sie in diesem Wissen den Leistungseinbruch rechtfertigen, nur um den try..catch-Block zu missbrauchen? Ich sage nein. Ich kann mir keinen einzigen Grund vorstellen, warum die Front-End-Welt damit vollkommen zufrieden zu sein scheint.
Nachdem ich nun meine Argumentation erläutert habe, kehren wir zum Hauptthema zurück.
Das Problem im Detail
Eine HTTP-Antwort kann je nach Statuscode unterschiedliche Informationen enthalten. Beispielsweise kann ein Todo-Endpunkt wie api/todos/:id, der eine PATCH-HTTP-Anfrage empfängt, eine Antwort mit einem anderen Text zurückgeben, wenn der Statuscode der Antwort 200 ist, als wenn der Statuscode der Antwort 400 ist.
Lass es uns veranschaulichen:
// For the 200 response, a copy of the updated object: { "id": 123, "text": "The updated text" } // For the 400 response, a list of validation errors: { "errors": [ "The updated text exceeds the maximum allowed number of characters." ] }
Vor diesem Hintergrund kehren wir zur Problemstellung zurück: Wie kann ich eine Funktion eingeben, die diese PATCH-Anfrage ausführt, wobei TypeScript mir je nach HTTP-Statuscode beim Schreiben mitteilen kann, mit welchem Body ich es zu tun habe? Code? Die Antwort: Verwenden Sie eine fließende Syntax (Builder-Syntax, verkettete Syntax), um Typen zu akkumulieren.
Die Lösung entwickeln
Beginnen wir mit der Definition eines Typs, der auf einem vorherigen Typ aufbaut:
export type AccumType<T, NewT> = T | NewT;
Super einfach: Verbinde die gegebenen Typen T und NewT zu einem neuen Typ. Verwenden Sie diesen neuen Typ erneut als T in AccumType<>, und Sie können dann einen weiteren neuen Typ akkumulieren. Das per Hand zu machen ist allerdings nicht schön. Lassen Sie uns ein weiteres Schlüsselelement für die Lösung vorstellen: Fließende Syntax.
Fließende Syntax
Angenommen ein Objekt der Klasse X, dessen Methoden sich immer selbst (oder eine Kopie von sich selbst) zurückgeben, kann man Methodenaufrufe nacheinander verketten. Dies ist eine fließende Syntax oder verkettete Syntax.
Lassen Sie uns eine einfache Klasse schreiben, die Folgendes tut:
export class NamePending<T> { accumulate<NewT>() { return this as NamePending<AccumType<T, NewT>>; } } // Now you can use it like this: const x = new NamePending<{ a: number; }>(); // x is of type NamePending<{ a: number; }>. const y = x.accumulate<{ b: string; }> // y is of type NamePending<{ a: number; } | { b: string; }>.
Eureka! Wir haben die fließende Syntax und den von uns geschriebenen Typ erfolgreich kombiniert, um mit der Akkumulation von Datentypen in einem einzigen Typ zu beginnen!
Falls es nicht offensichtlich ist, können Sie die Übung fortsetzen, bis Sie die gewünschten Typen angesammelt haben (x.accumulate().accumulate()… bis Sie fertig sind).
Das ist alles gut und schön, aber dieser supereinfache Typ bindet den HTTP-Statuscode nicht an den entsprechenden Body-Typ.
Verfeinern, was wir haben
Was wir wollen, ist, TypeScript mit genügend Informationen zu versorgen, damit seine Funktion zur Typeingrenzung greift. Dazu tun wir das Notwendige, um Code zu erhalten, der für das ursprüngliche Problem relevant ist (Eingabe von Texten von HTTP-Antworten in eine per -Statuscodebasis).
Zunächst benennen Sie AccumType um und entwickeln Sie es weiter. Der folgende Code zeigt den Fortschritt in Iterationen:
// Iteration 1. export type FetchResult<T, NewT> = T | NewT; // Iteration 2. export type FetchResponse<TStatus extends number, TBody> = { ok: boolean; status: TStatus; statusText: string; body: TBody }; export type FetchResult<T, TStatus extends number, NewT> = T | FetchResponse<TStatus, NewT>; //Makes sense to rename NewT to TBody.
An diesem Punkt wurde mir etwas klar: Statuscodes sind endlich: Ich kann (und habe) sie nachschlagen und Typen für sie definieren und diese Typen verwenden, um den Typparameter TStatus einzuschränken:
// Iteration 3. export type OkStatusCode = 200 | 201 | 202 | ...; export type ClientErrorStatusCode = 400 | 401 | 403 | ...; export type ServerErrorStatusCode = 500 | 501 | 502 | ...; export type StatusCode = OkStatusCode | ClientErrorStatusCode | ServerErrorStatusCode; export type NonOkStatusCode = Exclude<StatusCode, OkStatusCode>; export type FetchResponse<TStatus extends StatusCode, TBody> = { ok: TStatus extends OkStatusCode ? true : false; status: TStatus; statusText: string; body: TBody }; export type FetchResult<T, TStatus extends StatusCode, TBody> = T | FetchResponse<TStatus, TBody>;
Wir sind bei einer Reihe von Typen angelangt, die einfach wunderschön sind: Durch Verzweigen (Schreiben von if-Anweisungen) basierend auf Bedingungen für die ok- oder die status-Eigenschaft wird die Typeingrenzungsfunktion von TypeScript aktiviert! Wenn Sie es nicht glauben, schreiben wir den Unterrichtsteil und probieren es aus:
export class DrFetch<T> { for<TStatus extends StatusCode, TBody>() { return this as DrFetch<FetchResult<T, TStatus, TBody>>; } }
Probefahren Sie dies:
// For the 200 response, a copy of the updated object: { "id": 123, "text": "The updated text" } // For the 400 response, a list of validation errors: { "errors": [ "The updated text exceeds the maximum allowed number of characters." ] }
Jetzt sollte klar sein, warum die Typeingrenzung die Form des Körpers beim Verzweigen basierend auf der ok-Eigenschaft der Statuseigenschaft korrekt vorhersagen kann.
Es gibt jedoch ein Problem: Die anfängliche Typisierung der Klasse, wenn sie instanziiert wird, markiert im Kommentarblock oben. Ich habe es so gelöst:
export type AccumType<T, NewT> = T | NewT;
Diese kleine Änderung macht das anfängliche Tippen praktisch überflüssig, und wir sind jetzt im Geschäft!
Jetzt können wir Code wie den folgenden schreiben und Intellisense wird 100 % genau sein:
export class NamePending<T> { accumulate<NewT>() { return this as NamePending<AccumType<T, NewT>>; } } // Now you can use it like this: const x = new NamePending<{ a: number; }>(); // x is of type NamePending<{ a: number; }>. const y = x.accumulate<{ b: string; }> // y is of type NamePending<{ a: number; } | { b: string; }>.
Typeingrenzung funktioniert auch bei der Abfrage der ok-Eigenschaft.
Wenn Sie es nicht bemerkt haben: Wir konnten viel besseren Code schreiben, indem wir keine Fehler auslösten. Nach meiner beruflichen Erfahrung ist Axios falsch, Ky ist falsch und jeder andere Fetch-Helfer da draußen, der das Gleiche tut, ist falsch.
Abschluss
TypeScript macht tatsächlich Spaß. Durch die Kombination von TypeScript und fließender Syntax können wir so viele Typen wie nötig ansammeln, sodass wir vom ersten Tag an genaueren und klareren Code schreiben können, nicht erst nach mehrmaligem Debuggen. Diese Technik hat sich als erfolgreich erwiesen und kann von jedem ausprobiert werden. Installieren Sie dr-fetch und testen Sie es:
// Iteration 1. export type FetchResult<T, NewT> = T | NewT; // Iteration 2. export type FetchResponse<TStatus extends number, TBody> = { ok: boolean; status: TStatus; statusText: string; body: TBody }; export type FetchResult<T, TStatus extends number, NewT> = T | FetchResponse<TStatus, NewT>; //Makes sense to rename NewT to TBody.
Ein komplexeres Paket
Ich habe auch wj-config erstellt, ein Paket, das auf die vollständige Beseitigung der veralteten .env-Dateien und dotenv abzielt. Dieses Paket verwendet ebenfalls den hier gelehrten TypeScript-Trick, verknüpft jedoch Typen mit & und nicht mit |. Wenn Sie es ausprobieren möchten, installieren Sie v3.0.0-beta.1. Allerdings sind die Eingaben deutlich komplexer. dr-fetch nach wj-config zu erstellen war ein Kinderspiel.
Lustiges Zeug: Was es da draußen gibt
Sehen wir uns einige der Fehler an, die es in Abruf-bezogenen Paketen gibt.
isomorpher Abruf
Sie können dies in der README-Datei sehen:
// Iteration 3. export type OkStatusCode = 200 | 201 | 202 | ...; export type ClientErrorStatusCode = 400 | 401 | 403 | ...; export type ServerErrorStatusCode = 500 | 501 | 502 | ...; export type StatusCode = OkStatusCode | ClientErrorStatusCode | ServerErrorStatusCode; export type NonOkStatusCode = Exclude<StatusCode, OkStatusCode>; export type FetchResponse<TStatus extends StatusCode, TBody> = { ok: TStatus extends OkStatusCode ? true : false; status: TStatus; statusText: string; body: TBody }; export type FetchResult<T, TStatus extends StatusCode, TBody> = T | FetchResponse<TStatus, TBody>;
„Schlechte Antwort vom Server“?? Nein. „Der Server sagt, Ihre Anfrage sei fehlerhaft.“ Ja, der Wurfteil selbst ist schrecklich.
ts-fetch
Dieser hat die richtige Idee, kann aber leider nur OK- oder Nicht-OK-Antworten eingeben (maximal 2 Typen).
ky
Eines der Pakete, das ich am meisten kritisiert habe, zeigt dieses Beispiel:
export class DrFetch<T> { for<TStatus extends StatusCode, TBody>() { return this as DrFetch<FetchResult<T, TStatus, TBody>>; } }
Das würde ein sehr junger Entwickler schreiben: Nur der glückliche Weg. Die Äquivalenz laut README:
const x = new DrFetch<{}>(); // Ok, having to write an empty type is inconvenient. const y = x .for<200, { a: string; }>() .for<400, { errors: string[]; }>() ; /* y's type: DrFetch<{ ok: true; status: 200; statusText: string; body: { a: string; }; } | { ok: false; status: 400; statusText: string; body: { errors: string[]; }; } | {} // <-------- WHAT IS THIS!!!??? > */
Der Teil des Werfens ist so schlimm: Warum solltest du dich zum Werfen verzweigen, um dich später zum Fangen zu zwingen? Für mich macht es überhaupt keinen Sinn. Auch der Text im Fehler ist irreführend: Es handelt sich nicht um einen „Abruffehler“. Der Abruf hat funktioniert. Du hast eine Antwort bekommen, nicht wahr? Es hat dir einfach nicht gefallen ... weil es nicht der glückliche Weg ist. Eine bessere Formulierung wäre „HTTP-Anfrage fehlgeschlagen:“. Was fehlgeschlagen ist, war die Anfrage selbst, nicht der Abrufvorgang.
Das obige ist der detaillierte Inhalt vonSo verwenden Sie TypeScript zum Akkumulieren von Typen: Eingabe ALLER möglichen fetch()-Ergebnisse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Häufig gestellte Fragen und Lösungen für das Ticket-Ticket-Ticket-Ticket in Front-End im Front-End-Entwicklungsdruck ist der Ticketdruck eine häufige Voraussetzung. Viele Entwickler implementieren jedoch ...

JavaScript ist der Eckpfeiler der modernen Webentwicklung. Zu den Hauptfunktionen gehören eine ereignisorientierte Programmierung, die Erzeugung der dynamischen Inhalte und die asynchrone Programmierung. 1) Ereignisgesteuerte Programmierung ermöglicht es Webseiten, sich dynamisch entsprechend den Benutzeroperationen zu ändern. 2) Die dynamische Inhaltsgenerierung ermöglicht die Anpassung der Seiteninhalte gemäß den Bedingungen. 3) Asynchrone Programmierung stellt sicher, dass die Benutzeroberfläche nicht blockiert ist. JavaScript wird häufig in der Webinteraktion, der einseitigen Anwendung und der serverseitigen Entwicklung verwendet, wodurch die Flexibilität der Benutzererfahrung und die plattformübergreifende Entwicklung erheblich verbessert wird.

Es gibt kein absolutes Gehalt für Python- und JavaScript -Entwickler, je nach Fähigkeiten und Branchenbedürfnissen. 1. Python kann mehr in Datenwissenschaft und maschinellem Lernen bezahlt werden. 2. JavaScript hat eine große Nachfrage in der Entwicklung von Front-End- und Full-Stack-Entwicklung, und sein Gehalt ist auch beträchtlich. 3. Einflussfaktoren umfassen Erfahrung, geografische Standort, Unternehmensgröße und spezifische Fähigkeiten.

Diskussion über die Realisierung von Parallaxe -Scrolling- und Elementanimationseffekten in diesem Artikel wird untersuchen, wie die offizielle Website der Shiseeido -Website (https://www.shiseeido.co.jp/sb/wonderland/) ähnlich ist ...

Zu den neuesten Trends im JavaScript gehören der Aufstieg von Typenkripten, die Popularität moderner Frameworks und Bibliotheken und die Anwendung der WebAssembly. Zukunftsaussichten umfassen leistungsfähigere Typsysteme, die Entwicklung des serverseitigen JavaScript, die Erweiterung der künstlichen Intelligenz und des maschinellen Lernens sowie das Potenzial von IoT und Edge Computing.

JavaScript zu lernen ist nicht schwierig, aber es ist schwierig. 1) Verstehen Sie grundlegende Konzepte wie Variablen, Datentypen, Funktionen usw. 2) Beherrschen Sie die asynchrone Programmierung und implementieren Sie sie durch Ereignisschleifen. 3) Verwenden Sie DOM -Operationen und versprechen Sie, asynchrone Anfragen zu bearbeiten. 4) Vermeiden Sie häufige Fehler und verwenden Sie Debugging -Techniken. 5) Die Leistung optimieren und Best Practices befolgen.

Wie fusioniere ich Array -Elemente mit derselben ID in ein Objekt in JavaScript? Bei der Verarbeitung von Daten begegnen wir häufig die Notwendigkeit, dieselbe ID zu haben ...

Datenaktualisierungsprobleme in Zustand Asynchronen Operationen. Bei Verwendung der Zustand State Management Library stoßen Sie häufig auf das Problem der Datenaktualisierungen, die dazu führen, dass asynchrone Operationen unzeitgemäß sind. � ...
