TypeScript - Generische Einschränkungen: Die Macht flexibler Typen entfesseln

Hallo da draußen, zukünftige TypeScript-Zauberer! Heute begeben wir uns auf eine aufregende Reise in die Welt der Generischen Einschränkungen. Keine Sorge, wenn du neu im Programmieren bist – ich werde dein freundlicher Guide sein, und wir werden dieses Thema Schritt für Schritt angehen. Am Ende dieses Tutorials wirst du Generics wie ein Profi einschränken können!

TypeScript - Generic Constraints

Was sind Generische Einschränkungen?

Bevor wir ins Detail gehen, lassen Sie uns mit einer einfachen Analogie beginnen. Stell dir vor, du hast eine magische Box, die alle Arten von Gegenständen aufnehmen kann. Das ist im Wesentlichen, was ein Generic in TypeScript ist – ein flexibler Behälter für verschiedene Typen. Was wäre, wenn wir einige Regeln darauf legen möchten, was in diese Box kann? Genau hier kommen die generischen Einschränkungen ins Spiel!

Generische Einschränkungen erlauben es uns, die Typen zu beschränken, die mit unseren Generics verwendet werden können. Es ist wie das Anbringen einer Etikette an unserer magischen Box, die sagt: "Nur Objekte mit einer 'length'-Eigenschaft erlaubt!"

Problembeispiele: Warum brauchen wir Generische Einschränkungen?

Schauen wir uns einige Szenarien an, in denen generische Einschränkungen den Tag retten können:

Beispiel 1: Die geheimnisvolle 'length'-Eigenschaft

function getLength<T>(item: T): number {
return item.length; // Fehler: Eigenschaft 'length' existiert nicht auf Typ 'T'
}

Ups! TypeScript gibt uns einen Fehler. Warum? Weil nicht alle Typen eine length-Eigenschaft haben. Was wäre, wenn wir dieser Funktion eine Zahl übergeben? Zahlen haben keine Länge!

Beispiel 2: Der verwirrende Vergleich

function compareValues<T>(value1: T, value2: T): boolean {
return value1 > value2; // Fehler: Operator '>' kann nicht auf Typen 'T' und 'T' angewendet werden
}

Noch ein Fehler! TypeScript weiß nicht, ob T mit > verglichen werden kann. Was wäre, wenn wir Zeichenfolgen übergeben? Oder komplexe Objekte?

Diese Beispiele zeigen uns, warum wir generische Einschränkungen brauchen. Sie helfen uns, präziser und fehlerfreier Code zu schreiben.

Wie Generische Einschränkungen in TypeScript funktionieren

Nun sehen wir, wie wir generische Einschränkungen verwenden können, um unsere Probleme zu lösen:

Das magische 'extends'- Schlüsselwort

Um eine Einschränkung hinzuzufügen, verwenden wir das extends-Schlüsselwort. Es ist so, als würde TypeScript sagen: "Hey, dieser Typ muss mindestens diese Eigenschaften oder Fähigkeiten haben!"

Lassen Sie uns unsere getLength-Funktion beheben:

interface Lengthwise {
length: number;
}

function getLength<T extends Lengthwise>(item: T): number {
return item.length; // Kein Fehler mehr!
}

Lassen Sie uns das herunterbrechen:

  1. Wir definieren eine Schnittstelle Lengthwise, die eine length-Eigenschaft hat.
  2. Wir verwenden <T extends Lengthwise>, um zu sagen "T muss mindestens das haben, was Lengthwise hat".
  3. Jetzt weiß TypeScript, dass was auch immer T ist, es definitiv eine length-Eigenschaft haben wird!

Lassen Sie es ausprobieren:

console.log(getLength("Hello")); // Funktioniert! Zeichenfolgen haben Länge
console.log(getLength([1, 2, 3])); // Funktioniert! Arrays haben Länge
console.log(getLength(123)); // Fehler! Zahlen haben keine Länge

Ist das nicht toll? Wir haben erfolgreich unser Generic eingeschränkt!

Typenparameter in Generischen Einschränkungen verwenden

Manchmal möchten wir einen Typenparameter basierend auf einem anderen einschränken. Es ist, als würde man sagen: "Diese Box kann nur Gegenstände aufnehmen, die kompatibel sind mit dem, was bereits drin ist."

Schauen wir uns ein Beispiel an:

function copyProperties<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = source[id];
}
return target;
}

Was passiert hier?

  1. Wir haben zwei Typenparameter: T und U.
  2. T extends U bedeutet, dass T mindestens alles ist, was U ist, aber es kann mehr sein.
  3. Dies ermöglicht es uns, Eigenschaften von source nach target zu kopieren, in dem Wissen, dass target alle Eigenschaften hat, die source hat.

Lassen Sie es in Aktion sehen:

interface Person {
name: string;
}

interface Employee extends Person {
employeeId: number;
}

let person: Person = { name: "Alice" };
let employee: Employee = { name: "Bob", employeeId: 123 };

copyProperties(employee, person); // Funktioniert!
copyProperties(person, employee); // Fehler! Person hat keine employeeId

Praktische Anwendungen und Best Practices

Nun, da wir verstehen, wie generische Einschränkungen funktionieren, schauen wir uns einige realweltliche Anwendungen und Best Practices an:

  1. Einschränkung auf Objekttypen: Oftentimes, you'll want to ensure you're working with objects:
function cloneObject<T extends object>(obj: T): T {
return { ...obj };
}
  1. Einschränkung auf Funktionstypen: You can ensure a type is callable:
function invokeFunction<T extends Function>(func: T): void {
func();
}
  1. Einschränkung auf spezifische Eigenschaften: Ensure objects have specific properties:
function getFullName<T extends { firstName: string; lastName: string }>(obj: T): string {
return `${obj.firstName} ${obj.lastName}`;
}
  1. Mehrere Einschränkungen: You can apply multiple constraints using the & operator:
function processData<T extends number & { toFixed: Function }>(data: T): string {
return data.toFixed(2);
}

Hier ist eine Tabelle, die diese Methoden zusammenfasst:

Methode Beschreibung Beispiel
Objekt-Einschränkung Stellt sicher, dass der Typ ein Objekt ist <T extends object>
Funktionseinschränkung Stellt sicher, dass der Typ aufrufbar ist <T extends Function>
Eigenschaftseinschränkung Stellt sicher, dass der Typ spezifische Eigenschaften hat <T extends { prop: Type }>
Mehrfache Einschränkungen Kombiniert mehrere Einschränkungen <T extends TypeA & TypeB>

Schlussfolgerung: Die Macht der Einschränkungen nutzen

Glückwunsch! Du hast ein mächtiges Werkzeug in deinem TypeScript-Werkzeugkasten entsperrt. Generische Einschränkungen erlauben es uns, flexiblen, aber typensicheren Code zu schreiben und das Beste aus beiden Welten zu haben.

Denk daran, der Schlüssel zum Beherrschen von generischen Einschränkungen ist die Übung. Versuche, einige deiner bestehenden Codes in Generics und Einschränkungen umzuwandeln. Du wirst überrascht sein, wie sauberer und robuster dein Code wird!

Als wir uns verabschieden, hier ist ein kleiner Programmierwitz für dich: Warum ist der TypeScript-Entwickler pleite gegangen? Weil er zu viele generische Einschränkungen verwendet und keine Art von Zahlung akzeptieren konnte! ?

Weiter codieren, weiter lernen und vor allem: Viel Spaß mit TypeScript!

Credits: Image by storyset