Best Practices in der TypeScript-Programmierung

Best Practices in der TypeScript-Programmierung

Dieser Artikel erklärt Best Practices in der TypeScript-Programmierung.

Dieser Leitfaden erklärt praktische Best Practices, um die Typen von TypeScript zu nutzen, um Fehler zu reduzieren und besser lesbaren Code zu schreiben.

YouTube Video

Best Practices in der TypeScript-Programmierung

Der größte Vorteil von TypeScript ist 'Fehler durch Typen zu verhindern und die Absicht des Codes deutlich zu machen'.

Best Practices sind nicht bloß Regeln, sondern eine Sammlung von Prinzipien, um sicheren, lesbaren und wartbaren Code zu schreiben. Im Folgenden stellen wir gängige TypeScript-Best Practices mit praktischen Beispielen vor.

Vermeiden Sie any und geben Sie den Typen immer sinnvolle Definitionen

Schauen wir uns zunächst den Punkt an, 'any zu vermeiden und aussagekräftige Typen zu verwenden.'.

any schaltet die Typüberprüfung vollständig ab, was den Zweck von TypeScript zunichte macht. Anstatt any zu verwenden, nur um Dinge zum Laufen zu bringen, ist es wichtig, so beschreibende Typen wie möglich zu definieren.

1// Bad
2function parse(data: any) {
3  return data.value;
4}

Dieser Code kann jeden Wert akzeptieren, daher können Laufzeitfehler nicht verhindert werden.

1// Good
2type ParsedData = {
3  value: string;
4};
5
6function parse(data: ParsedData): string {
7  return data.value;
8}

Durch das Definieren von Typen machen Sie die Absicht von Eingaben und Ausgaben deutlich und erhöhen die Sicherheit.

Definieren Sie stets Objektstrukturen explizit mit type oder interface.

Als Nächstes betrachten wir den Punkt, 'Objektstrukturen immer explizit mit type oder interface zu definieren.'.

Wenn Sie Objekte ad hoc verwenden, kann deren Struktur unklar werden. Extrahieren Sie Typen immer für Wiederverwendbarkeit und Wartbarkeit.

1// Bad
2function createUser(user: { name: string; age: number }) {
3  console.log(user.name);
4}

Selbst in kleinen Codeabschnitten ist es wichtig, sich die Gewohnheit anzueignen, Typen zu trennen.

1// Good
2type User = {
3  name: string;
4  age: number;
5};
6
7function createUser(user: User): void {
8  console.log(user.name);
9}

Durch das Benennen von Typen wird das Verständnis der gesamten Codebasis deutlich erleichtert.

Verwenden Sie Union-Typen, um alle möglichen Zustände präzise abzubilden.

Wenn Sie rohe string- oder number-Typen für Bedingungen verwenden, können unerwartete Werte durchrutschen. Durch die Verwendung von Union-Typen können nur die erlaubten Zustände auf Typ-Ebene dargestellt werden.

1// Bad
2function setStatus(status: string) {
3  console.log(status);
4}

In diesem Code können bedeutungslose Zeichenketten oder falsche Werte zur Kompilierzeit nicht erkannt werden.

1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4function setStatus(status: Status): void {
5  console.log(status);
6}

Durch die Verwendung von Union-Typen können 'unmögliche Zustände' zuverlässig zur Kompilierzeit ausgeschlossen werden. Dadurch erhöht sich die Sicherheit von Verzweigungen sowie die Zuverlässigkeit des Codes.

Behandeln Sie null und undefined explizit.

Als Nächstes sehen wir uns den Punkt an, 'null und undefined explizit zu behandeln.'.

In TypeScript ist es wichtig, das mögliche Fehlen eines Werts im Typ auszudrücken. Wenn Sie Dinge vage lassen, kann dies zu Laufzeitfehlern führen.

1type User = {
2  name: string;
3  email?: string;
4};

email könnte nicht existieren, daher sollten Sie davon ausgehen und entsprechend damit umgehen.

1function printEmail(user: User): void {
2  if (user.email) {
3    console.log(user.email);
4  }
5}

Prüfen Sie optionale Werte immer vor ihrer Verwendung.

Verwenden Sie Typ-Assertions (as) nur als letzte Möglichkeit.

Als Nächstes betrachten wir den Punkt, 'Typ-Assertions nicht übermäßig zu verwenden.'.

Typ-Assertions umgehen vorübergehend die Typprüfung von TypeScript, um anzugeben: 'Ich weiß, dass dieser Wert diesen Typ hat.'. Eine Überbenutzung untergräbt die Typsicherheit.

1// Bad
2const value = input as string;

In diesem Code tritt kein Fehler auf, selbst wenn der tatsächliche Wert kein String ist, was zu Laufzeitfehlern führen kann. Wie im folgenden Code gezeigt, sollte zuerst eine sichere Überprüfung mittels Type Guards erfolgen.

1// Good
2function isString(value: unknown): value is string {
3  return typeof value === "string";
4}
5
6if (isString(input)) {
7  console.log(input.toUpperCase());
8}

Type Guards sind ein Mechanismus, mit dem beim Überprüfen des tatsächlichen Werts der Typ sicher bestimmt werden kann. Indem Sie Type Guards gegenüber Type Assertions bevorzugen, können Laufzeitfehler leichter vermieden werden.

Verlassen Sie sich nicht zu sehr auf die Typinferenz bei Rückgabewerten.

Als Nächstes werfen wir einen Blick auf den Punkt, 'sich nicht zu sehr auf die Inferenz von Rückgabetypen zu verlassen.'.

TypeScripts Typinferenz ist zwar mächtig, aber es ist sicherer, Rückgabetypen öffentlicher Funktionen explizit zu annotieren. Dies minimiert die Auswirkungen zukünftiger Änderungen.

1// Bad
2function sum(a: number, b: number) {
3  return a + b;
4}

Geben Sie Ihre Absicht auch bei kleinen Funktionen klar an.

1// Good
2function sum(a: number, b: number): number {
3  return a + b;
4}

Das explizite Festlegen von Rückgabetypen erhöht die Stabilität Ihrer API.

Behandeln Sie Eingaben sicher, indem Sie unknown verwenden.

Schauen wir uns als Nächstes an, 'externe Eingaben sicher mit unknown entgegenzunehmen.'.

Für externe Eingaben wie APIs, JSON oder Benutzereingaben sollte unknown anstelle von any verwendet werden. Dadurch wird gewährleistet, dass alle Werte überprüft werden und die Typsicherheit erhalten bleibt.

1// Bad
2function handleResponse(data: any) {
3  console.log(data.id);
4}

So können Sie Typen mit unknown validieren.

 1// Good
 2function handleResponse(data: unknown): void {
 3  if (
 4    typeof data === "object" &&
 5    data !== null &&
 6    "id" in data
 7  ) {
 8    console.log((data as { id: number }).id);
 9  }
10}

unknown kann nicht direkt verwendet werden; es handelt sich um einen Typ, der validiert werden muss. Dies ist besonders wirksam beim Umgang mit externen Eingaben.

Erhöhen Sie die Ausdruckskraft, indem Sie kleine Typen kombinieren.

Als Nächstes betrachten wir den Punkt, 'die Ausdrucksstärke durch das Kombinieren kleiner Typen zu erhöhen.'.

Das auf einmal definieren großer Typen reduziert die Lesbarkeit und Wartbarkeit. Teilen Sie Ihre Typen in sinnvolle Einheiten auf und kombinieren Sie sie nach Bedarf.

 1type Id = number;
 2
 3type UserProfile = {
 4  id: Id;
 5  name: string;
 6};
 7
 8type UserWithStatus = UserProfile & {
 9  status: "active" | "inactive";
10};

Typen als Komponenten zu betrachten, hilft beim Strukturieren Ihres Designs.

type und interface

Vorteile von interface

type und interface können beide Typen definieren, aber ihre beabsichtigte Verwendung und ihre Eigenschaften unterscheiden sich. Wenn Sie sie für die passenden Anwendungsfälle einsetzen, wird die Absicht Ihrer Typdefinitionen klarer.

 1// Bad
 2type User = {
 3  id: number;
 4  name: string;
 5};
 6
 7type AdminUser = {
 8  id: number;
 9  name: string;
10  role: "admin";
11};

Wenn Sie gemeinsame Teile auf diese Weise duplizieren, wird Ihr Code anfällig für Änderungen.

1// Good
2interface User {
3  id: number;
4  name: string;
5}
6
7interface AdminUser extends User {
8  role: "admin";
9}

interface ist ideal für Entwürfe, die Erweiterungen (extends) beinhalten, und eignet sich am besten, um die 'Struktur' von Objekten auszudrücken.

Vorteile von type

Hingegen ist type ausdrucksstärker und besser geeignet, um Union- und Intersection-Typen zu verwenden.

1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4type ApiResponse<T> =
5  | { status: "success"; data: T }
6  | { status: "error"; message: string };

type eignet sich gut, um Zustände, Optionen und Kombinationen auszudrücken.

Empfehlungen zur Auswahl zwischen type und interface

Als Faustregel gilt: Verwenden Sie interface für Objektstrukturen und Schnittstellen, und type, wenn Sie die Ausdruckskraft von Union-, Intersection- oder Typoperationen benötigen.

Beide funktionieren ähnlich, aber es ist wichtig, je nach dem Zweck und der Intention des Typs zu wählen.

Verstehen Sie Ihre Typen als Dokumentation.

Abschließend betrachten wir den Punkt, 'Typen als Dokumentation zu schreiben.'.

Gute Typdefinitionen vermitteln mehr Informationen, als es Kommentare je könnten. Es ist wichtig, einen Zustand anzustreben, in dem 'die Spezifikation allein durch das Betrachten der Typen verstanden werden kann.'.

1type ApiError = {
2  code: number;
3  message: string;
4  retryable: boolean;
5};

Auf diese Weise ist eine der größten Stärken von TypeScript, dass Typdefinitionen als eine Art Spezifikationsdokument dienen können.

Zusammenfassung

TypeScript-Best Practices bedeuten nicht, übertriebene Strenge walten zu lassen. Das Wesentliche ist, die Absicht über Typen zu verdeutlichen und Code zu schreiben, der robust gegenüber Änderungen ist.

Durch das Anhäufen kleiner Regeln im täglichen Arbeiten können Sie langfristig Effekte wie 'einfachere Code-Reviews', 'weniger Fehler' und 'besseres Verständnis für Sie selbst und andere in der Zukunft' erreichen.

Indem Sie sich zunächst fragen, 'wie kann ich das mit Typen ausdrücken?', schreiben Sie qualitativ hochwertigen Code im TypeScript-Stil.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video