{"id":556,"date":"2023-02-17T20:37:12","date_gmt":"2023-02-17T19:37:12","guid":{"rendered":"https:\/\/rising-bits.com\/?p=556"},"modified":"2023-02-17T20:37:15","modified_gmt":"2023-02-17T19:37:15","slug":"generics-in-typescript-teil-2","status":"publish","type":"post","link":"https:\/\/rising-bits.com\/en\/generics-in-typescript-teil-2\/","title":{"rendered":"Generics in TypeScript (Teil 2)"},"content":{"rendered":"\n<p>Im <a href=\"https:\/\/rising-bits.com\/generics-in-typescript-teil-1\/\" data-type=\"post\" data-id=\"550\">ersten Teil<\/a> \u00fcber Generics haben wir bereits Identit\u00e4tsfunktionen erstellt, die auf verschiedene Typen angewendet werden k\u00f6nnen. In diesem Artikel werden wir uns mit der Art der Funktionen selbst und der Erstellung von generischen Schnittstellen befassen.<\/p>\n\n\n\n<p>Die Typisierung von generischen Funktionen erfolgt analog zu nicht-generischen Funktionen, wobei die Typ-Parameter zuerst aufgef\u00fchrt werden, \u00e4hnlich wie bei Funktionsdeklarationen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">function identity&lt;Type>(arg: Type): Type {\n  return arg;\n}\n \nlet myIdentity: &lt;Type>(arg: Type) => Type = identity;<\/code><\/pre>\n\n\n\n<p>Es w\u00e4re ebenso m\u00f6glich gewesen, in der Typdefinition einen anderen Namen f\u00fcr den generischen Typ-Parameter zu verwenden, solange die Anzahl der Typvariablen und deren Verwendung \u00fcbereinstimmen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">function identity&lt;Type>(arg: Type): Type {\n  return arg;\n}\n \nlet myIdentity: &lt;Input>(arg: Input) => Input = identity;<\/code><\/pre>\n\n\n\n<p>Es ist auch m\u00f6glich, den generischen Typ als Aufruf-Signatur in einem Objektliteral-Typ zu definieren:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">function identity&lt;Type>(arg: Type): Type {\n  return arg;\n}\n \nlet myIdentity: { &lt;Type>(arg: Type): Type } = identity;<\/code><\/pre>\n\n\n\n<p>Dies f\u00fchrt uns zur Erstellung unserer ersten generischen Schnittstelle. Wir k\u00f6nnen das Objektliteral aus dem vorherigen Beispiel als Vorlage nutzen und es in eine Schnittstelle \u00fcberf\u00fchren:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">interface GenericIdentityFn {\n  &lt;Type>(arg: Type): Type;\n}\n \nfunction identity&lt;Type>(arg: Type): Type {\n  return arg;\n}\n \nlet myIdentity: GenericIdentityFn = identity;<\/code><\/pre>\n\n\n\n<p>In \u00e4hnlichen F\u00e4llen k\u00f6nnen wir den generischen Parameter als Teil der gesamten Schnittstelle definieren. Dadurch wird sichtbar, welche Typen f\u00fcr die Generizit\u00e4t verwendet werden (z. B. <code>Dictionary&lt;string><\/code> anstelle von nur <code>Dictionary<\/code>). Der Typ-Parameter ist somit f\u00fcr alle anderen Mitglieder der Schnittstelle sichtbar.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">interface GenericIdentityFn&lt;Type> {\n  (arg: Type): Type;\n}\n \nfunction identity&lt;Type>(arg: Type): Type {\n  return arg;\n}\n \nlet myIdentity: GenericIdentityFn&lt;number> = identity;<\/code><\/pre>\n\n\n\n<p>In unserem Beispiel hat sich etwas ge\u00e4ndert. Statt eine generische Funktion zu beschreiben, haben wir jetzt eine nicht-generische Funktionssignatur, die Teil eines generischen Typs ist. Wenn wir <code>GenericIdentityFn<\/code> verwenden, m\u00fcssen wir auch das entsprechende Typargument (hier: <code>number<\/code>) angeben, um festzulegen, welche Signatur verwendet wird. Es ist wichtig zu verstehen, wann der Typparameter direkt auf die Aufrufsignatur und wann auf die Schnittstelle selbst angewendet werden sollte, um zu beschreiben, welche Aspekte eines Typs generisch sind.<\/p>\n\n\n\n<p>Zus\u00e4tzlich zu generischen Schnittstellen k\u00f6nnen wir auch generische Klassen erstellen. Beachte, dass es nicht m\u00f6glich ist, generische <code>Enums<\/code> und <code>Namespaces<\/code> zu erstellen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Generische Klassen<\/h2>\n\n\n\n<p>Generische Klassen haben \u00e4hnlich wie generische Schnittstellen eine Struktur mit einer Liste von generischen Typ-Parametern in spitzen Klammern (<code>&lt;><\/code>) nach dem Klassennamen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">class GenericNumber&lt;NumType> {\n  zeroValue: NumType;\n  add: (x: NumType, y: NumType) => NumType;\n}\n \nlet myGenericNumber = new GenericNumber&lt;number>();\nmyGenericNumber.zeroValue = 0;\nmyGenericNumber.add = function (x, y) {\n  return x + y;\n};<\/code><\/pre>\n\n\n\n<p>Dies ist eine sehr w\u00f6rtliche Verwendung der Klasse <code>GenericNumber<\/code>, aber man k\u00f6nnte bemerkt haben, dass sie nicht darauf beschr\u00e4nkt ist, nur den Typ <code>number<\/code> zu verwenden. Wir k\u00f6nnten stattdessen auch <code>string<\/code> oder sogar komplexere Objekte verwenden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">let stringNumeric = new GenericNumber&lt;string>();\nstringNumeric.zeroValue = \"\";\nstringNumeric.add = function (x, y) {\n  return x + y;\n};\n \nconsole.log(stringNumeric.add(stringNumeric.zeroValue, \"test\"));<\/code><\/pre>\n\n\n\n<p>Wie es bei Schnittstellen der Fall ist, erm\u00f6glicht es uns auch bei Klassen, den Typ-Parameter direkt auf der Klasse zu setzen, um sicherzustellen, dass alle Eigenschaften der Klasse mit dem gleichen Typ arbeiten.<\/p>\n\n\n\n<p>Eine Klasse hat zwei Typseiten: die statische Seite und die Instanzseite. Generische Klassen sind nur generisch \u00fcber ihre Instanzseite, nicht \u00fcber ihre statische Seite, sodass bei der Arbeit mit Klassen die statischen Elemente nicht den Typ-Parameter der Klasse nutzen k\u00f6nnen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Generische Einschr\u00e4nkungen<\/h2>\n\n\n\n<p>Wenn man sich an ein fr\u00fcheres Beispiel erinnert, m\u00f6chte man m\u00f6glicherweise manchmal eine generische Funktion schreiben, die auf einer Menge von Typen arbeitet, bei denen Du bestimmte Kenntnisse dar\u00fcber hast, welche F\u00e4higkeiten diese Typen besitzen werden. In unserem Beispiel der Funktion <code>loggingIdentity<\/code> wollten wir auf die <code>.length<\/code>-Eigenschaft von <code>arg<\/code> zugreifen, aber der Compiler konnte nicht garantieren, dass jeder Typ eine <code>.length<\/code>-Eigenschaft hat. Aus diesem Grund warnte uns der Compiler, dass wir diese Annahme nicht treffen k\u00f6nnen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">function loggingIdentity&lt;Type>(arg: Type): Type {\n  console.log(arg.length);\nProperty 'length' does not exist on type 'Type'.\n  return arg;\n}<\/code><\/pre>\n\n\n\n<p>Anstatt mit allen m\u00f6glichen Typen zu arbeiten, m\u00f6chten wir diese Funktion auf Typen beschr\u00e4nken, die auch die <code>.length<\/code>-Eigenschaft haben. Solange der Typ diese Eigenschaft besitzt, erlauben wir ihn, aber es ist erforderlich, dass er mindestens diese Eigenschaft hat. Dazu m\u00fcssen wir unsere Anforderung als Einschr\u00e4nkung f\u00fcr den Typ angeben.<\/p>\n\n\n\n<p>Daf\u00fcr erstellen wir eine Schnittstelle, die unsere Anforderung beschreibt. Wir erstellen eine Schnittstelle mit einer einzelnen <code>.length<\/code>-Eigenschaft und verwenden das Schl\u00fcsselwort <code>extends<\/code>, um unsere Einschr\u00e4nkung auszudr\u00fccken.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">interface Lengthwise {\n  length: number;\n}\n \nfunction loggingIdentity&lt;Type extends Lengthwise>(arg: Type): Type {\n  console.log(arg.length); \/\/ Now we know it has a .length property, so no more error\n  return arg;\n}<\/code><\/pre>\n\n\n\n<p>Aufgrund der Einschr\u00e4nkung der generischen Funktion wird sie nicht mehr f\u00fcr alle Typen funktionieren:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">loggingIdentity(3);\n\/\/ Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.<\/code><\/pre>\n\n\n\n<p>Stattdessen m\u00fcssen wir Werte \u00fcbergeben, deren Typ alle erforderlichen Eigenschaften besitzt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">loggingIdentity({ length: 10, value: 3 });<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Verwendung von Typ-Parametern in generischen Einschr\u00e4nkungen<\/h2>\n\n\n\n<p>Wenn Du einen Typ-Parameter deklarierst, kannst Du ihn durch einen anderen Typ-Parameter einschr\u00e4nken. Angenommen, Du m\u00f6chtest einer Funktion den Namen einer Eigenschaft eines Objekts \u00fcbergeben und die dazugeh\u00f6rige Eigenschaft abrufen. Damit Du sicherstellen kannst, dass die Eigenschaft auch tats\u00e4chlich auf dem Objekt existiert, setzt Du eine Einschr\u00e4nkung zwischen den beiden Typen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">function getProperty&lt;Type, Key extends keyof Type>(obj: Type, key: Key) {\n  return obj[key];\n}\n \nlet x = { a: 1, b: 2, c: 3, d: 4 };\n \ngetProperty(x, \"a\");\ngetProperty(x, \"m\");\n\/\/ Argument of type '\"m\"' is not assignable to parameter of type '\"a\" | \"b\" | \"c\" | \"d\"'.<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Verwendung von Klassen-Typen in Generics<\/h2>\n\n\n\n<p>Wenn Sie in TypeScript Generics verwenden, um <code>Factories<\/code> zu erstellen, m\u00fcssen Sie Klassentypen anhand ihrer Konstruktionsfunktionen referenzieren. Ein Beispiel daf\u00fcr ist:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">function create&lt;Type>(c: { new (): Type }): Type {\n  return new c();\n}<\/code><\/pre>\n\n\n\n<p>Ein Beispiel mit h\u00f6herem Schwierigkeitsgrad verwendet die Prototyp-Eigenschaft, um Beziehungen zwischen der Konstruktionsfunktion und der Instanzseite von Klassentypen zu erschlie\u00dfen und zu begrenzen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript\">class BeeKeeper {\n  hasMask: boolean = true;\n}\n \nclass ZooKeeper {\n  nametag: string = \"Mikle\";\n}\n \nclass Animal {\n  numLegs: number = 4;\n}\n \nclass Bee extends Animal {\n  keeper: BeeKeeper = new BeeKeeper();\n}\n \nclass Lion extends Animal {\n  keeper: ZooKeeper = new ZooKeeper();\n}\n \nfunction createInstance&lt;A extends Animal>(c: new () => A): A {\n  return new c();\n}\n \ncreateInstance(Lion).keeper.nametag;\ncreateInstance(Bee).keeper.hasMask;<\/code><\/pre>\n\n\n\n<p>Herzlichen Gl\u00fcckwunsch, Du hast es geschafft! Nachdem Du zwei unserer Blog-Artikel zum Thema Generics in TypeScript gelesen hast, bist Du nun in der Lage, die vielf\u00e4ltigen M\u00f6glichkeiten der Generics-Programmierung zu nutzen. In den Artikeln haben wir uns intensiv mit der Verwendung von Typ-Parametern, generischen Schnittstellen und der Einschr\u00e4nkung von Typen befasst. Wir haben auch gelernt, wie wir Klassentypen in Generics einbeziehen und Beziehungen zwischen der Konstruktionsfunktion und der Instanzseite von Klassentypen ableiten und einschr\u00e4nken k\u00f6nnen.<\/p>\n\n\n\n<p>All diese Konzepte werden in der Welt der TypeScript-Programmierung h\u00e4ufig eingesetzt, um wiederverwendbaren, flexiblen und robusten Code zu erstellen. Sie helfen uns, die Wartbarkeit von Code zu verbessern und die Entwicklungszeit zu verk\u00fcrzen. Wir hoffen, dass Du unsere Artikel genossen hast und dass sie dir einen umfassenden \u00dcberblick \u00fcber das Thema Generics in TypeScript gegeben haben.<\/p>\n\n\n\n<p>Jetzt bist Du bereit, diese Konzepte in deiner eigenen Arbeit anzuwenden und eine neue Welt der flexiblen und wiederverwendbaren Programmierung zu entdecken. Vielen Dank f\u00fcrs Lesen und viel Erfolg bei deinen zuk\u00fcnftigen Projekten!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im ersten Teil \u00fcber Generics haben wir bereits Identit\u00e4tsfunktionen erstellt, die auf verschiedene Typen angewendet werden k\u00f6nnen. In diesem Artikel [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":557,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[106],"tags":[],"class_list":["post-556","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-clean-code"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/mlmwwmq2k3up.i.optimole.com\/w:auto\/h:auto\/q:mauto\/f:best\/https:\/\/i0.wp.com\/rising-bits.com\/wp-content\/uploads\/2023\/02\/Screenshot-2023-02-17-at-20.36.24.png?fit=1598%2C692&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts\/556","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/comments?post=556"}],"version-history":[{"count":1,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts\/556\/revisions"}],"predecessor-version":[{"id":558,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts\/556\/revisions\/558"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/media\/557"}],"wp:attachment":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/media?parent=556"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/categories?post=556"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/tags?post=556"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}