Natürlich können Sie alle Teile Ihrer Applikation von Grund auf selbst erstellen – die Netzwerk- und Datenbankschichten auf dem Server, ein Framework für die Benutzerschnittstelle und die Zustandsverwaltung im Frontend. Tun Sie das besser nicht, denn die Details sind nicht immer einfach umzusetzen. Zum Glück haben andere Programmierer die schweren Probleme bei Front- und Backend bereits gelöst. Durch die Verwendung bestehender Werkzeuge, Bibliotheken und Frameworks können wir in der Entwicklung schnell iterieren und eigene Applikationen mit wenig Aufwand auf ein stabiles Fundament stellen.
In diesem Kapitel geht es um einige der beliebtesten Werkzeuge und Frameworks, die häufige Probleme bei Client und Server lösen. Wir besprechen, welches Framework für welche Aufgaben am besten geeignet ist und wie Sie es sicher in Ihre TypeScript-Applikation integrieren können.
TypeScript ist von Natur aus gut für Frontend-Applikationen geeignet. Durch seine reichhaltige Unterstützung für JSX und seine Fähigkeit, Mutabilität sicher zu modellieren, versorgt TypeScript Ihre Applikation mit Struktur und Sicherheit und erleichtert es so, auch in der schnelllebigen Welt der Frontend-Entwicklung korrekten und wartbaren Code zu schreiben.
Natürlich sind alle eingebauten DOM-APIs typsicher. Um sie aus TypeScript zu verwenden, müssen Sie nur die nötigen Typdeklarationen in die tsconfig.json: Ihres Projekts einbinden:
{
"compilerOptions": {
"lib": ["dom", "es2015"]
}
}
Hiermit weisen Sie TypeScript an, die Datei lib.dom.d.ts – also die eingebauten Browser- und DOM-Typdeklarationen – beim Typechecking Ihres Codes zu verwenden.
Die lib-Option in tsconfig.json weist TypeScript an, bei der Verarbeitung des Codes in Ihrem Projekt bestimmte Typdeklarationen einzubinden. Hierdurch wird kein zusätzlicher Code oder JavaScript erzeugt, der zur Laufzeit existiert. Die Option kann beispielsweise nicht dafür sorgen, dass das DOM auf magische Weise in einer NodeJS-Umgebung funktioniert (Ihr Code wird kompiliert, aber zur Laufzeit fehlschlagen) – Sie müssen selbst dafür sorgen, dass Ihre Typdeklarationen mit den Dingen zusammenpassen, die Ihre JavaScript-Umgebung zur Laufzeit tatsächlich unterstützt. Weitere Informationen finden Sie unter »Das TypeScript-Projekt erstellen« auf Seite 253. |
Sobald die DOM-Typdeklarationen aktiviert sind, können Sie DOM- und Browser-APIs sicher nutzen, um Dinge zu tun wie das hier:
// Eigenschaften des globalen window-Objekts lesen
let model = {
url: window.location.href
}
// Ein <input />-Element erzeugen
let input = document.createElement('input')
// Das Element mit ein paar CSS-Klassen versehen
input.classList.add('Input', 'URLInput')
// Das Modell aktualisieren, wenn der Benutzer etwas eingibt
input.addEventListener('change', () =>
model.url = input.value.toUpperCase()
)
// Das <input />-Element ins DOM einfügen
document.body.appendChild(input)
Danach wird der gesamte Code dem Typechecking unterzogen und besitzt interne Leckerbissen wie die automatische Vervollständigung im Editor. Hier ein Beispiel:
document.querySelector('.Element').value // Error TS2339: Property 'value' does
// not exist on type 'Element'.
TypeScript löst einen Fehler aus, weil der Rückgabetyp von querySelector nicht nullwertfähig ist.
Für einfache Frontend-Applikationen reichen diese schlichten DOM-APIs aus und geben Ihnen, was Sie brauchen, um sicher und typgetrieben für den Browser zu programmieren. In der wahren Welt verwenden Frontend-Applikationen dagegen ein Framework, um das DOM-Rendering (und Re-rendering), Datenbindungen und Events zu abstrahieren. In den folgenden Abschnitten finden Sie Hinweise zur effektiven Verwendung von TypeScript mit einigen der beliebten Browser-Frameworks.
React ist aktuell eines der beliebtesten Frontend-Frameworks und eine sehr gute Wahl, wenn es um Typsicherheit geht.
Der Grund für die Sicherheit ist, dass die React-Komponenten, also die Grundbausteine von React-Applikationen, in TypeScript nicht nur definiert, sondern auch verwendet (»konsumiert«) werden, wodurch beide Bereiche dem Typechecking unterzogen werden. Diese Eigenschaft gibt es bei Frontend-Frameworks eher selten. Sie können Typen verwenden, um z.B. zu sagen: »Diese Komponente übernimmt eine Benutzer-ID und eine Farbe.« oder »Diese Komponente kann nur Listenelemente als Kindelemente haben.« Diese Beschränkungen werden dann von TypeScript erzwungen, das sicherstellt, dass Ihre Komponenten wirklich tun, was sie sollen.
Die Sicherheit um Komponentendefinitionen und -consumer – die View-Schicht einer Frontend-Applikation – ist ein echtes Killerfeature. Der View ist üblicherweise der Teil, in dem Tippfehler, fehlende Attribute, falsch geschriebene Parameter und falsch verschachtelte Elemente dafür sorgen, dass Programmierer Tausende Stunden damit verbringen, sich die Haare auszureißen, während die Browserfenster immer wieder neu geladen werden. Sobald Sie Ihre Views mit TypeScript und React typisieren, wird sich Ihre Produktivität (und die Ihres Teams) verdoppeln.
Bei der Verwendung von React definieren Sie Ihre Views mit einer speziellen DSL namens JavaScript XML (JSX), die direkt in den JavaScript-Code eingebettet wird. Das sieht ein wenig wie HTML in Ihrem JavaScript aus. Danach schieben Sie Ihr JavaScript durch den JSX-Compiler, der die JSX-Syntax in reguläre JavaScript-Funktionsaufrufe umwandelt.
Dazu ein Beispiel. Angenommen, Sie brauchen eine Menü-App für das Restaurant eines englischen Freunds und erstellen mit folgendem JSX eine Liste mit ein paar Gerichten vom Brunch-Menü:
<ul class='list'>
<li>Homemade granola with yogurt</li>
<li>Fantastic french toast with fruit</li>
<li>Tortilla Española with salad</li>
</ul>
Nachdem Sie den Code mit einem JSX-Compiler behandelt haben, z.B. Babels transform-react-jsx-Plug-in (http://bit.ly/2uENY4M), erhalten Sie folgende Ausgabe:
React.createElement(
'ul',
{'class': 'list'},
React.createElement(
'li',
'Homemade granola with yogurt'
),
React.createElement(
'li',
null,
'Fantastic French toast with fruit'
),
React.createElement(
'li',
null,
'Tortilla Española with salad'
)
);
TSC-Flag: esModuleInterop Da JSX zu einem Aufruf von React.createElement kompiliert wird, sollten Sie unbedingt dafür sorgen, dass die React-Bibliothek in jede Datei importiert wird, in der Sie JSX verwenden. Dadurch stellen Sie sicher, dass sich eine Variable namens React im aktuellen Geltungsbereich befindet: import React from 'react' |
|
|
Keine Sorge. Falls nicht, wird TypeScript Sie warnen: <ul /> // Error TS2304: Cannot find name 'React'. |
|
Beachten Sie außerdem, dass ich in meiner tsconfig.json die Einstellung {"esModuleInterop": true} vorgenommen habe, damit React ohne die Verwendung des Wildcard-Zeichens (*) importiert werden kann. Ohne diese Anweisung sähe der Import von React so aus: import * as React from 'react' |
Das Gute an JSX ist, dass Ihr Code wie normales HTML aussieht und Sie diesen automatisch in ein JavaScript-Engine-freundliches Format kompilieren können. Als Programmierer verwenden Sie nur die deklarative High-Level-DSL und müssen sich nicht mit Implementierungsdetails herumschlagen.
Um mit React zu arbeiten, brauchen Sie kein JSX (Sie können den kompilierten Code auch selbst schreiben, und er funktioniert genauso gut). Anders herum können Sie JSX auch ohne React verwenden (der Funktionsaufruf React.createElement, zu dem die JSX-Tags kompiliert werden, ist konfigurierbar). Die Kombination aus beiden ist dagegen geradezu magisch und macht das Schreiben von Views zu einem Vergnügen und zudem noch sehr, sehr sicher.
Dateien, die JSX enthalten, verwenden die Dateiendung .jsx. TypeScript-Dateien, die JSX enthalten, verwenden die Endung .tsx. TSX verhält sich zu JSX wie TypeScript zu JavaScript: Es ist eine Sicherheits- und Hilfsschicht, die während der Kompilierung greift und Ihnen hilft, produktiver zu sein und weniger Fehler zu machen. Um die TSX-Unterstützung für Ihr Projekt zu aktivieren, fügen Sie folgende Zeilen zu Ihrer tsconfig.json hinzu:
{
"compilerOptions": {
"jsx": "react"
}
}
Die jsx-Direktive besaß beim Schreiben dieses Buchs drei verschiedene Modi:
react
JSX wird unter Verwendung des JSX-Pragmas (standardmäßig React.createElement) zu einer .js-Datei kompiliert.
react-native
JSX ohne Kompilierung erhalten, aber eine Datei mit .js-Endung ausgeben.
preserve
Typechecking für JSX durchführen, aber nicht wegkompilieren. Datei mit .jsx-Endung ausgeben.
Hinter den Kulissen stellt TypeScript einige leicht integrierbare Hooks für die Typisierung von TSX bereit. Dies sind spezielle Typen im global.JSX-Namensraum, den TypeScript als »Quelle der Wahrheit« für TSX-Typen in Ihrem Programm betrachtet. Wenn Sie nur React verwenden, müssen Sie gar nicht so tief gehen. Wenn Sie dagegen eine eigene TypeScript-Bibliothek erstellen, die TSX (aber nicht React) verwendet – oder wenn Sie einfach nur neugierig sind, wie die React-Typdeklarationen funktionieren –, dann finden Sie weitere Informationen unter Anhang G.
Mit React können zwei Arten von Komponenten deklariert werden: Funktionskomponenten und Klassenkomponenten. Beide Arten übernehmen bestimmte Eigenschaften und rendern TSX. Aus Sicht des Consumers sind sie identisch.
Die Deklaration und das Rendern von Funktionskomponenten sieht so aus:
import React from 'react'
type Props = {
isDisabled?: boolean
size: 'Big' | 'Small'
text: string
onClick(event: React.MouseEvent<HTMLButtonElement>): void
}
export function FancyButton(props: Props) {
const [toggled, setToggled] = React.useState(false)
return <button
className={'Size-' + props.size}
disabled={props.isDisabled || false}
onClick={event => {
setToggled(!toggled)
}}
>{props.text}</button>
}
let button = <FancyButton
size='Big'
text='Sign Up Now'
onClick={() => console.log('Clicked!')}
/>
Das ist alles. TypeScript sorgt dabei für Folgendes:
Eine Klassenkomponente sieht so ähnlich aus:
import React from 'react'
import {FancyButton} from './FancyButton'
type Props = {
firstName: string
userId: string
}
type State = {
isLoading: boolean
}
class SignupForm extends React.Component<Props, State> {
state = {
isLoading: false
}
render() {
return <>
<h2>Sign up for a 7-day supply of our tasty
toothpaste now, {this.props.firstName}.</h2>
<FancyButton
isDisabled={this.state.isLoading}
size='Big'
text='Sign Up Now'
onClick={this.signUp}
/>
</>
}
private signUp = async () => {
this.setState({isLoading: true})
try {
await fetch('/api/signup?userId=' + this.props.userId)
} finally {
this.setState({isLoading: false})
}
}
}
let form = <SignupForm firstName='Albert' userId='13ab9g3' />
Wie Sie sehen, haben wir in unserem Beispiel wertbasierte (FancyButton, SignupForm) und intrinsische Komponenten (section, h2) miteinander vermischt. Dabei benutzen wir TypeScript, um zum Beispiel Folgendes zu verifizieren:
Mit TypeScript können Sie Ihren React-Code sicherer machen. Das wird Sie zwangsläufig zu einem besseren und glücklicheren Menschen machen.
Wir haben hier nicht das PropTypes-Feature von React genutzt. Dies ist eine Möglichkeit, Prop-Typen während der Laufzeit zu deklarieren und zu überprüfen. Da TypeScript die Typen für uns bereits bei der Kompilierung überprüft, müssen wir das nicht noch einmal tun. |
Ein Beitrag von Shyam Seshadri
Angular hat einen größeren Funktionsumfang als React. Es kann nicht nur Views rendern, es ist auch in der Lage, Netzwerkanfragen zu verschicken und zu verwalten, es beherrscht Routing und Dependency Injection. Es wurde von Grund auf für die Zusammenarbeit mit TypeScript entwickelt (tatsächlich ist das Framework selbst in TypeScript geschrieben).
Ein zentraler Bestandteil der Arbeitsweise von Angular ist der in Angular CLI (die Kommandozeilen-Schnittstelle) integrierte Ahead-of-Time-(AoT-)Compiler. Er benutzt die von Ihnen übergebenen Typinformationen, um den Code in reguläres JavaScript zu kompilieren. Anstatt TypeScript direkt aufzurufen, wendet Angular einen ganzen Sack voller Optimierungen und Transformierungen auf Ihren Code an, bevor es schließlich an TypeScript übergibt und ihn in JavaScript kompiliert.
Wir wollen einmal sehen, wie Angular TypeScript und seinen AoT-Compiler benutzt, um ein sicheres Schreiben von Frontend-Applikationen zu ermöglichen.
Um ein neues Angular-Projekt zu initialisieren, muss zunächst global das Angular-CLI über NPM installiert werden:
npm install @angular/cli --global
Danach können Sie das Angular-CLI benutzen, um eine neue Angular-Applikation zu initialisieren:
ng new my-angular-app
Wenn Sie den Anweisungen folgen, richtet Angular-CLI ein einfaches Angular-Applikations-Grundgerüst (»scaffold«) für Sie ein.
Wir werden hier nicht im Einzelnen auf die Struktur, Konfiguration oder Ausführung einer Angular-Applikation eingehen. Detaillierte Informationen finden Sie in der offiziellen Angular-Dokumentation (https://angular.io/docs).
Jetzt wollen wir eine Angular-Komponente erstellen. Sie haben große Ähnlichkeit mit React-Komponenten und können für die Beschreibung der DOM-Struktur, Stildefinitionen und des Controllers der Komponente verwendet werden. Bei Angular erzeugen Sie das Grundgerüst der Komponente mit dem Angular CLI und ergänzen die Details danach von Hand. Eine Angular-Komponente besteht aus mehreren Dateien:
Wir beginnen mit der Komponentenklasse:
import {Component, OnInit} from '@angular/core'
@Component({
styleUrls: ['./simple-message.component.css'],
templateUrl: './simple-message.component.html'
})
export class SimpleMessageComponent implements OnInit {
message: string
ngOnInit() {
this.message = 'No messages, yet'
}
}
Im Großen und Ganzen ist das eine ziemlich normale TypeScript-Klasse. Allerdings gibt es auch ein paar Unterschiede dabei, wie Angular TypeScript nutzt. Das sind:
TSC-Flag: fullTemplateTypeCheck Um das Typechecking für Angular-Templates zu aktivieren (und das sollten Sie!), setzen Sie fullTemplateTypeCheck in Ihrer tsconfig.json auf true: { "angularCompilerOptions": { "fullTemplateTypeCheck": true } } |
|
|
Beachten Sie, dass angularCompilerOptions keine Optionen für TSC selbst festlegt. Stattdessen werden hiermit Compiler-Flags für den AoT-Compiler von Angular definiert. |
Angular besitzt einen eingebauten Dependency Injector (DI, ein »Abhängigkeits-Einfüger«). Dadurch kann das Framework Dienste instanziieren und als Argumente an abhängige Komponenten und Dienste weitergeben. Das kann die Instanziierung und das Testen von Diensten erleichtern.
Zur Illustration wollen wir SimpleMessageComponent so erweitern, dass eine Abhängigkeit namens MessageService eingefügt wird, die Nachrichten vom Server holt:
import {Component, OnInit} from '@angular/core'
import {MessageService} from '../services/message.service'
@Component({
selector: 'simple-message',
templateUrl: './simple-message.component.html',
styleUrls: ['./simple-message.component.css']
})
export class SimpleMessageComponent implements OnInit {
message: string
constructor(
private messageService: MessageService
) {}
ngOnInit() {
this.messageService.getMessage().subscribe(response =>
this.message = response.message
)
}
}
Der Angular-AoT-Compiler sieht sich die Parameter des Konstruktors Ihrer Komponente an und pflückt sich ihre Typen heraus (z.B. MessageService). Dann durchsucht er die Abhängigkeitsliste des entsprechenden Dependency Injectors nach einer Abhängigkeit des angegebenen Typs. Falls noch nicht geschehen, instanziiert er daraufhin diese Abhängigkeit (per new) und übergibt sie an den Konstruktor der SimpleMessageComponent-Instanz. Dieses ganze DI-Zeug ist ziemlich kompliziert. Es kann aber sehr bequem sein, wenn Ihre Applikation wächst und Sie, je nach ihrer Konfiguration, mehrere verschiedene Abhängigkeiten verwenden (z.B. ProductionAPIService oder DevelopmentAPIService) oder die Applikation testen wollen (MockAPIService).
Werfen wir schnell noch einen Blick darauf, wie ein Dienst definiert wird:
import {Injectable} from '@angular/core'
import {HttpClient} from '@angular/common/http'
@Injectable({
providedIn: 'root'
})
export class MessageService {
constructor(private http: HttpClient) {}
getMessage() {
return this.http.get('/api/message')
}
}
Immer wenn wir in Angular einen Dienst erstellen, benutzen wir erneut TypeScript-Dekoratoren, um sie als etwas zu registrieren, dass Injectable (einfügbar) ist. Außerdem definieren wir, ob der Dienst auf Root-Ebene oder als Submodul bereitgestellt wird. Hier haben wir den Dienst MessageService registriert. Dadurch können wir ihn an beliebiger Stelle in die Applikation einfügen. Im Konstruktor einer beliebigen Komponente oder eines Diensts können wir einfach einen MessageService anfordern, und Angular kümmert sich auf magische Weise um seine Integration.
Nachdem wir diese beiden beliebten Frontend-Frameworks aus dem Weg geschafft haben, können wir uns jetzt darum kümmern, die Schnittstelle zwischen Front- und Backend zu typisieren.
Ein Beitrag von Nick Nance
Unabhängig davon, für welche Front- und Backend-Frameworks Sie sich entscheiden, werden Sie auf sichere Weise zwischen Rechnern kommunizieren wollen – von Client zu Server, von Server zu Client, von Server zu Server, von Client zu Client.
In diesem Bereich sind die Softwarewerkzeuge eher dünn gesät. Bevor wir darauf eingehen, welche das sind, wollen wir überlegen, wie wir unsere eigene Lösung erstellen würden und welche Vor- und Nachteile das haben würde (schließlich sind wir Software-Ingenieure).
Wir wollen folgendes Problem lösen: Selbst wenn unsere Clients und Server 100 % typsicher sind – wahre Bastionen der Sicherheit –, müssen sie irgendwann über untypisierte Netzwerkprotokolle wie HTTP, TCP oder andere Socket-basierte Protokolle miteinander kommunizieren. Wie können wir diese Kommunikation typsicher machen?
Ein guter Startpunkt könnte ein typsicheres Protokoll sein, z.B. das in »Typsichere Protokolle« auf Seite 196 entwickelte. Eine Möglichkeit könnte so aussehen:
type Request =
| {entity: 'user', data: User}
| {entity: 'location', data: Location}
// client.ts
async function get<R extends Request>(entity: R['entity']): Promise<R['data']> {
let res = await fetch(/api/${entity})
let json = await res.json()
if (!json) {
throw ReferenceError('Empty response')
}
return json
}
// app.ts
async function startApp() {
let user = await get('user') // User
}
Sie könnten aufeinander abgestimmte post- und put-Funktionen schreiben, die mit Ihrer REST-API kommunizieren und für jede vom Server unterstützte Entity einen passenden Typ hinzufügen. Im Backend implementieren Sie für jede Entity einen entsprechenden Handler, der dem jeweiligen Client die gewünschten Daten aus der Datenbank zurückgibt.
Was passiert aber, wenn Ihr Server nicht in TypeScript geschrieben ist oder Sie Ihren Request-Typ nicht zwischen Client und Server austauschen können (wodurch beide nach und nach die Synchronisierung verlieren). Was passiert, wenn Sie nicht REST, sondern beispielsweise GraphQL verwenden, oder wenn zusätzliche Clients unterstützt werden müssen, zum Beispiel Swift-Clients unter iOS oder Java-Clients unter Android?
An dieser Stelle kommen typisierte, Code-generierte APIs ins Spiel. Es gibt sie in vielen verschiedenen Geschmacksrichtungen. Jede besitzt Bibliotheken in verschiedenen Sprachen (inklusive TypeScript), beispielsweise:
Diese Werkzeuge verlassen sich auf eine »gemeinsame Quelle der Wahrheit« für Server und Clients – Datenmodelle für Swagger, GraphQL-Schemata für Apollo, Protokollpuffer für gRPC –, die dann in sprachspezifische Bindungen für die jeweils verwendete Sprache kompiliert werden (in unserem Fall TypeScript).
Diese Codeerzeugung verhindert, dass weder Client noch Server (oder mehrere Clients) die Synchronisierung verlieren. Da alle Plattformen ein gemeinsames Schema nutzen, wird der Fall nicht mehr auftreten, in dem Sie Ihre iOS-App aktualisiert haben, um ein bestimmtes Feld zu unterstützen, aber vergessen haben, beim Pull-Request »Merge« anzuklicken, um auch die nötige Server-Unterstützung hinzuzufügen.
Um jedes Framework im Detail zu betrachten, bräuchten wir ein eigenes Buch. Suchen Sie sich ein Framework für Ihr Projekt aus und lesen Sie die jeweilige Dokumentation, um mehr zu erfahren.
Soll eine Applikation mit einer Datenbank kommunizieren, könnten Sie mit ein paar einfachen SQL- oder API-Aufrufen anfangen, die erst einmal nicht typisiert sind:
// PostgreSQL, verwendet hier node-postgres
let client = new Client
let res = await client.query(
'SELECT name FROM users where id = $1',
[739311]
) // 'any'
// MongoDB, verwendet hier node-mongodb-native
db.collection('users')
.find({id: 739311})
.toArray((err, user) =>
// user ist 'any'
)
Mit nur wenig Tipparbeit können Sie diese APIs absichern und die meisten der anys loswerden:
db.collection('users')
.find({id: 739311})
.toArray((err, user: User) =>
// user ist 'any'
)
Trotzdem sind reine SQL-APIs ziemlich krude. Leicht verwendet man den falschen Typ oder vergisst ihn ganz, und schon hat man eine Reihe von anys.
Und da kommen objektrelationale Mapper (ORMs) ins Spiel. ORMs verwenden Ihr Datenbankschema, um daraus Code zu erzeugen. Dadurch erhalten Sie eine High-Level-API, über die Abfragen, Aktualisierungen und Löschungen etc. vorgenommen werden können. In statisch typisierten Sprachen sind diese APIs typsicher, und Sie müssen sich nicht darum kümmern, ob etwas korrekt typisiert ist oder generische Typparameter manuell gebunden werden müssen.
Wenn Sie über TypeScript auf Ihre Datenbank zugreifen, sollten Sie überlegen, einen ORM zu verwenden. Beim Schreiben dieses Buchs ist TypeORM (https://www.npmjs.com/package/typeorm) von Umed Khudoiberdiev der vollständigste ORM für TypeScript. Er unterstützt MySQL, PostgreSQL, Microsoft SQL Server, Oracle und sogar MongoDB. Eine Abfrage nach dem Vornamen eines Benutzer könnte mit TypeORM etwa so aussehen:
let user = await UserRepository
.findOne({id: 739311}) // User | undefined
Beachten Sie die High-Level-API, die sicher (indem sie beispielsweise SQL-Injection-Angriffe verhindert) und typsicher ist (weil wir wissen, was der Typ findOne zurückgibt, ohne es manuell annotieren zu müssen). Verwenden Sie bei der Arbeit mit Datenbanken grundsätzlich einen ORM. Es ist bequemer, und Sie werden seltener um vier Uhr morgens aus dem Bett geklingelt, wenn das saleAmount-Feld null ist, weil Sie es die Nacht zuvor mit orderAmount aktualisiert haben und ein Mitarbeiter Ihre Datenbankmigration schon mal durchgeführt hat, weil er schon auf Ihren Pull Request gewartet hat, während Sie unterwegs waren. Gegen Mitternacht ist der Pull Request dann fehlgeschlagen, obwohl die Migration erfolgreich war, und das Sales-Team in New York wachte auf, nur um festzustellen, dass sämtliche Kundenbestellungen genau null Dollar betragen (das ist einem ...»Freund«... wirklich passiert).
In diesem Kapitel haben wir eine Vielzahl an Punkten behandelt: das direkte Verändern des DOM, die Verwendung von React und Angular, die Verwendung von Werkzeugen, um APIs typsicher zu machen, wie Swagger, gRPC und GraphQL, sowie die Verwendung von TypeORM für sichere Interaktionen mit Ihrer Datenbank.
JavaScript-Frameworks ändern sich sehr schnell. Wenn Sie das hier lesen, sind die beschriebenen APIs und Frameworks vielleicht schon auf dem Weg, Museumsstücke zu werden. Benutzen Sie Ihre neu gefundene Intuition, um herauszufinden, welche Probleme typsichere Frameworks lösen. So können Sie die Arbeit anderer Leute zu Ihrem Vorteil nutzen und Ihren Code sicherer, abstrakter und modularer machen. In diesem Kapitel geht es nicht darum, zu zeigen, welches Framework 2019 das beste ist, sondern welche Art von Problemen besser mit Frameworks gelöst werden können.
Mit der Kombination aus typsicherem UI-Code, einer typisierten API-Schicht und einem typsicheren Backend können Sie ganze Kategorien von Fehlern aus Ihrer Applikation ausschließen und im Ergebnis nachts besser schlafen.