TypeScript

TypeScript-Grundlagen

Peter Vogel

TypeScript sollte überwiegend anhand der eigenen Vorzüge der Sprache beurteilt werden. Die TypeScript-Sprachspezifikation bezeichnet TypeScript als „syntaktisches Bonbon für JavaScript“. Das entspricht der Wahrheit und ist möglicherweise ein wesentlicher Schritt, um die Zielgruppe dieser Sprache zu erreichen – clientseitige Entwickler, die zurzeit JavaScript verwenden.

Außerdem müssen Sie JavaScript verstehen, bevor Sie TypeScript verstehen können. Tatsächlich beschreibt die Sprachspezifikation (unter bit.ly/1xH1m5B verfügbar) die TypeScript-Konstrukte im Hinblick auf den sich ergebenden JavaScript-Code. Es ist jedoch ebenso sinnvoll, TypeScript als eine eigenständige Sprache zu betrachten, die einige gemeinsame Funktionen mit JavaScript aufweist.

TypeScript ist beispielsweise wie C# eine Sprache, die Datentypen verwendet. Auf diese Weise stehen neben anderen Funktionen IntelliSense-Unterstützung und Überprüfungen zur Kompilierzeit zur Verfügung. Ebenso wie C# enthält TypeScript generische und lambda-Ausdrücke (oder deren Entsprechungen).

TypeScript ist aber natürlich nicht C#. Es ist ebenso wichtig, das Besondere an TypeScript zu verstehen, wie das Verständnis dafür wichtig ist, welche Gemeinsamkeiten TypeScript mit der serverseitigen Sprache besitzt, die Sie zurzeit verwenden. Das TypeScript-Typsystem unterscheidet sich von C# (und ist einfacher). TypeScript nutzt die Kenntnisse aus anderen Objektmodellen auf einzigartige Weise und behandelt Vererbung anders als C#. Da TypeScript in JavaScript kompiliert wird, gelten zahlreiche Grundlagen von TypeScript auch für JavaScript. Bei C# ist dies nicht der Fall.

Es stellt sich also die Frage, ob Sie Ihren clientseitigen Code in dieser Sprache oder in JavaScript schreiben sollten.

TypeScript verwendet Datentypen

TypeScript verfügt nicht über zahlreiche integrierte Datentypen, die Sie zum Deklarieren von Variablen verwenden können – nur „string“, „number“ und „boolean“. Diese drei Typen sind ein Untertyp des Typs „any“ (den Sie beim Deklarieren von Variablen ebenfalls verwenden können). Sie können Variablen, die mit diesen vier Typen deklariert wurden, anhand der Typen „null“ oder „undefined“ festlegen oder testen. Sie können Methoden auch als „void“ deklarieren und damit angeben, dass sie keinen Wert zurückgeben.

Das folgende Beispiel deklariert eine Variable als „string“:

var name: string;

Sie können dieses einfache Typsystem durch aufgezählte Werte und vier Arten von Objekttypen erweitern: Schnittstellen, Klassen, Arrays und Funktionen. Der folgende Code definiert z. B. eine Schnittstelle (eine Art von Objekttyp) mit dem Namen „ICustomerShort“. Die Schnittstelle enthält zwei Elemente: eine Eigenschaft namens „Id“ und eine Methode namens „CalculateDiscount“:

interface ICustomerShort
{
  Id: number;
  CalculateDiscount(): number;
}

Ebenso wie in C# können Sie Schnittstellen beim Deklarieren von Variablen und Rückgabetypen verwenden. Das folgende Beispiel deklariert die Variable „cs“ als den Typ „ICustomerShort“:

var cs: ICustomerShort;

Sie können auch Objekttypen als Klassen definieren. Diese können (im Gegensatz zu Schnittstellen) ausführbaren Code enthalten. Das folgende Beispiel definiert eine Klasse namens „CustomerShort“ mit einer Eigenschaft und einer Methode:

class CustomerShort
{
  FullName: string;
  UpdateStatus( status: string ): string
  {
    ...manipulate status... 
    return status;
  }
}

Ebenso wie mit den aktuelleren Versionen von C# ist es nicht erforderlich, Implementierungscode bereitzustellen, wenn Sie eine Eigenschaft definieren. Die einfache Deklaration des Namens und des Typs ist ausreichend. Klassen können (wie in Abbildung 1 gezeigt) mindestens eine Schnittstelle implementieren. Auf diese Weise wird meine Schnittstelle „ICustomerShort“ mit ihrer Eigenschaft meiner Klasse „CustomerShort“ hinzugefügt.

Abbildung 1 – Hinzufügen einer Schnittstelle zu einer Klasse

class CustomerShort implements ICustomerShort
{
  Id: number;
  FullName: string;
  UpdateStatus(status: string): string
  {
    ...manipulate status...
    return status;
  }
  CalculateDiscount(): number
  {
    var discAmount: number;
    ...calculate discAmount...
    return discAmount;
  }
}

Wie Abbildung 1 zeigt, ist die Syntax zum Implementieren einer Schnittstelle in TypeScript genauso einfach wie in C#. Zum Implementieren der Elemente der Schnittstelle fügen Sie einfach Elemente mit dem gleichen Namen hinzu, anstatt den Namen der Schnittstelle an die Elemente der relevanten Klasse zu binden. In diesem Beispiel habe ich einfach „Id“ und „CalculateDiscount“ der Klasse hinzugefügt, um „ICustomerShort“ zu implementieren. Mit TypeScript können Sie auch Objekttypliterale verwenden. Der folgende Code legt die Variable „cst“ auf ein Objektliteral fest, das eine Eigenschaft und eine Methode enthält:

var csl = {
            Age: 61,
            HaveBirthday(): number
          {
            return this.Age++;
          }
        };

Dieses Beispiel verwendet einen Objekttyp, um den Rückgabewert der Methode „UpdateStatus“ anzugeben:

UpdateStatus( status: string ): { 
  status: string; valid: boolean }
{
  return {status: "New",
          valid: true
         };
}

Abgesehen von Objekttypen (Klasse, Schnittstelle, Literal und Array) können Sie auch Funktionstypen definieren, die die Signatur einer Funktion beschreiben. Der folgende Code schreibt „CalculateDiscount“ aus meiner Klasse „CustomerShort“ so um, dass ein einzelner Parameter namens „discountAmount“ angenommen wird:

interface ICustomerShort
{
  Id: number;
  CalculateDiscount( discountAmount:
    ( discountClass: string, 
      multipleDiscount: boolean ) => number): number
}

Dieser Parameter wird mithilfe eines Funktionstyps definiert, der zwei Parameter (einen Parameter vom Typ „string“, einen Parameter vom Typ „boolean“) annimmt und eine Zahl zurückgibt. Wenn Sie mit C# entwickeln, fällt Ihnen ggf. auf, dass die Syntax einem lambda-Ausdruck sehr ähnlich ist.

Eine Klasse, die diese Schnittstelle implementiert, ähnelt dem in Abbildung 2 gezeigten Code.

Abbildung 2 – Diese Klasse implementiert die richtige Schnittstelle

class CustomerShort implements ICustomerShort
{
  Id: number;
  FullName: string;
  CalculateDiscount( discountedAmount:
    ( discountClass: string, 
      multipleDiscounts: boolean ) => number ): number
  {
    var discAmount: number;
    ...calculate discAmount...
    return discAmount;
  }
}

Ebenso wie die aktuellen Versionen von C# leitet TypeScript den Datentyp einer Variablen von dem Wert ab, mit dem die Variable initialisiert wird. In diesem Beispiel geht TypeScript davon aus, dass die Variable „myCust“ den Wert „CustomerShort“ aufweist:

var myCust= new CustomerShort();
myCust.FullName = "Peter Vogel";

Ebenso wie in C# können Sie Variablen mithilfe einer Schnittstelle deklarieren und die Variable dann auf ein Objekt festlegen, das diese Schnittstelle implementiert:

var cs: ICustomerShort;
cs = new CustomerShort();
cs.Id = 11;
cs.FullName = "Peter Vogel";

Schließlich können Sie auch Typparameter verwenden (die generischen Ausdrücken in C# sehr ähnlich sind), damit der aufrufende Code den zu verwendenden Datentyp angeben kann. In diesem Beispiel legt der Code, der die Klasse erstellt, den Datentyp der Eigenschaft „Id“ fest:

class CustomerTyped<T>
{
  Id: T;
}

Dieser Code legt den Datentyp der Eigenschaft „Id“ auf den Typ „string“ fest, bevor sie verwendet wird:

var cst: CustomerTyped<string>;
cst = new CustomerTyped<string>();
cst.Id = "A123";

Sie können diese Konstrukte zum Isolieren von Klassen, Schnittstellen und anderen öffentlichen Elementen sowie zum Vermeiden von Namenskonflikten innerhalb von Modulen deklarieren, die C#-Namespaces ähneln. Sie müssen diese Elemente, die Sie anderen Modulen zur Verfügung stellen möchten, mit dem Schlüsselwort „export“ kennzeichnen. Das Modul in Abbildung 3 exportiert zwei Schnittstellen und eine Klasse.

Abbildung 3 – Exportieren von zwei Schnittstellen und einer Klasse

module TypeScriptSample
{
  export interface ICustomerDTO
  {
    Id: number;
  }
  export interface ICustomerShort extends ICustomerDTO
  {
    FullName: string;
  }
  export class CustomerShort implements ICustomerShort
  {
    Id: number;
    FullName: string;
  }

Damit die exportierten Komponenten verwendet werden können, können Sie dem Komponentennamen den Modulnamen als Präfix voranstellen. Das folgende Beispiel zeigt dies:

var cs: TypeScriptSample.CustomerShort;

Sie können auch das TypeScript-Schlüsselwort „import“ zum Einrichten einer Verknüpfung mit dem Modul verwenden:

import tss = TypeScriptSample;
...
var cs:tss.CustomerShort;

TypeScript ist bei den Datentypen flexibel

Alle diese Beispiele sollten Ihnen vertraut sein, wenn Sie in C# programmieren. Eine Ausnahme stellen vielleicht die Umkehrung der Variablendeklarationen (Variablenname an erster Stelle, Datentyp an zweiter Stelle) und Objektliterale dar. Beinahe alle Datentypen in TypeScript sind jedoch optional. In der Spezifikation werden die Datentypen als „Anmerkungen“ beschrieben. Wenn Sie Datentypen auslassen (und TypeScript den Datentyp nicht ableitet), wird für Datentypen der Typ „any“ als Standardwert verwendet.

TypeScript erfordert auch keine strenge Datentypzuordnung. TypeScript verwendet eine in der Spezifikation als „strukturelle Untertypen“ bezeichnete Vorgehensweise, um die Kompatibilität zu ermitteln. Diese Vorgehensweise ähnelt dem häufig als „Duck Typing“ bezeichneten Verfahren. In TypeScript werden zwei Klassen als identisch betrachtet, wenn sie Elemente mit den gleichen Typen aufweisen. Die folgenden Klasse „Customer­Short “ implementiert z. B. eine Schnittstelle namens „ICustomerShort“:

interface ICustomerShort
{
  Id: number;
  FullName: string;
}
class CustomerShort implements ICustomerShort
{
  Id: number;
  FullName: string;
}

Die folgende Klasse namens „CustomerDeviant“ ähnelt meiner Klasse „CustomerShort“:

class CustomerDeviant
{
  Id: number;
  FullName: string;
}

Dank der strukturellen Untertypen kann ich „CustomerDevient“ mit Variablen verwenden, die in meiner Klasse „CustomerShort“ oder meiner Schnittstelle „ICustomerShort“ definiert werden. Diese Beispiele verwenden „CustomerDeviant“ austauschbar mit Variablen, die als „CustomerShort“ oder „ICustomerShort“ definiert werden:

var cs: CustomerShort;
cs = new CustomerDeviant
cs.Id = 11;
var csi: ICustomerShort;
csi = new CustomerDeviant
csi.FullName = "Peter Vogel";

Durch diese Flexibilität können Sie TypeScript-Objektliterale Variablen zuweisen, die als Klassen oder Schnittstellen deklariert werden, wenn diese strukturell kompatibel sind. Dies ist hier der Fall:

var cs: CustomerShort;
cs = {Id: 2,
      FullName: "Peter Vogel"
     }
var csi: ICustomerShort;
csi = {Id: 2,
       FullName: "Peter Vogel"
      }

Dies führt zu für TypeScript spezifischen Funktionen für offensichtliche Typen, Obertypen und Untertypen sowie zu allgemeinen Fragen zu Möglichkeiten der Zuordnung, die hier nicht behandelt werden. Diese Funktionen würden „CustomerDeviant“ z. B. ermöglichen, Elemente zu besitzen, die in „CustomerShort“ nicht vorhanden sind, ohne dass ein Fehler meines Beispielcodes auftritt.

TypeScript arbeitet mit Klassen

Die TypeScript-Spezifikation sagt über die Sprache aus, dass sie „das Klassenmuster [mithilfe] von Prototypketten zum Implementieren zahlreicher Variationen für objektorientierte Vererbungsmechanismen“ implementiert. In der Praxis bedeutet dies, dass TypeScript nicht nur Datentypen verwendet, sondern gewissermaßen objektorientiert ist.

Auf die gleiche Weise, wie eine C#-Schnittstelle von einer Basisschnittstelle erben kann, kann eine TypeScript-Schnittstelle eine andere Schnittstelle erweitern – selbst dann, wenn diese andere Schnittstelle in einem anderen Modul definiert ist. Dieses Beispiel erweitert die Schnittstelle „ICustomerShort“, um eine neue Schnittstelle namens „ICustomerLong“ zu erstellen:

interface ICustomerShort
{
  Id: number;
}
interface ICustomerLong extends ICustomerShort
{
  FullName: string;
}

Die Schnittstelle „ICustomerLong“ enthält zwei Elemente: „FullName“ und „Id“. In der zusammengeführten Schnittstelle werden die Elemente aus der Schnittstelle zuerst angezeigt. Daher ist die Schnittstelle „ICustomerLong“ mit der folgenden Schnittstelle identisch:

interface ICustomerLongPseudo
{
  FullName: string;
  Id: number;
}

Eine Klasse, die „ICustomerLong“ implementiert, benötigt beide Eigenschaften:

class CustomerLong implements ICustomerLong
{
  Id: number;
  FullName: string;
}

Klassen können andere Klassen auf die gleiche Weise erweitern, wie eine Schnittstelle eine andere Schnittstelle erweitern kann. Die Klasse in Abbildung 4 erweitert „CustomerShort“ und fügt der Definition eine neue Eigenschaft hinzu. Sie verwendet explizite Getter und Setter zum Definieren der Eigenschaften (wenn auch nicht auf besonders sinnvolle Weise).

Abbildung 4 – Mit Gettern und Settern definierte Eigenschaften

class CustomerShort
{
  Id: number;
}
class CustomerLong extends CustomerLong
{
  private id: number;
  private fullName: string;
  get Id(): number
  {
    return this.id
  }
  set Id( value: number )
  {
    this.id = value;
  }
  get FullName(): string
  {
    return this.fullName;
  }
  set FullName( value: string )
  {
    this.fullName = value;
  }
}

TypeScript setzt die bewährte Methode des Zugriffs auf interne Felder (wie „id“ und „fullName“) über einen Verweis auf die Klasse („this“) durch. Klassen können auch über Konstruktorfunktionen verfügen, die eine Funktion verwenden, die von C# vor Kurzem übernommen wurde: die automatische Definition von Feldern. Die Konstruktorfunktion in einer TypeScript-Klasse muss den Namen „constructor“ tragen, und ihre öffentlichen Parameter werden automatisch als Eigenschaften definiert und aus den an sie übergebenen Werten initialisiert. In diesem Beispiel nimmt der Konstruktor einen Parameter namens „Company“ vom Typ „string“ an:

export class CustomerShort implements ICustomerShort
{
  constructor(public Company: string)
  {       }

Da der Parameter „Company“ als öffentlich definiert ist, erhält die Klasse ebenfalls eine öffentliche Eigenschaft namens „Company“, die aus dem an den Kontruktor übergebenen Wert initialisiert wird. Dank dieser Funktion wird die Variable „comp“ auf „PH&VIS“ festgelegt. Das folgende Beispiel zeigt dies:

var css: CustomerShort;
css = new CustomerShort( "PH&VIS" );
var comp = css.Company;

Wenn der Parameter eines Konstruktors als privat deklariert wird, wird eine interne Eigenschaft erstellt, auf die nur über Code in Elementen der Klasse über das Schlüsselwort „this“ zugegriffen werden kann. Wenn der Parameter nicht als öffentlich oder privat deklariert wird, wird keine Eigenschaft generiert.

Ihre Klasse muss über einen Kontruktor verfügen. Wenn Sie keinen Konstruktor bereitstellen, wird wie in C# ein Konstruktor für Sie bereitgestellt. Wenn Ihre Klasse eine andere Klasse erweitert, muss jeder Konstruktor, den Sie erstellen, einen Aufruf von „super“ enthalten. Auf diese Weise wird der Konstruktor für die Klasse aufgerufen, die er erweitert. Dieses Beispiel enthält einen Konstruktor mit einem Aufruf von „super“, der Parameter für den Konstruktor der Basisklasse bereitstellt:

class MyBaseClass
{
  constructor(public x: number, public y: number ) { }   
}
class MyDerivedClass extends MyBaseClass
{
  constructor()
  {
    super(2,1);
  }
}

Die Vererbung von TypeScript funktioniert anders

Dies alles kommt Ihnen ebenfalls bekannt vor, wenn Sie in C# programmieren. Es werden nur einige eigenartige Schlüsselwörter („extends“) verwendet. Das Erweitern einer Klasse oder einer Schnittstelle ist jedoch nicht ganz der gleiche Vorgang wie die Vererbungsmechanismen in C#. Die TypeScript-Spezifikation verwendet die üblichen Begriffe für die Klasse, die erweitert wird („Basisklasse“), und die Klasse, die die Basisklasse erweitert („abgeleitete Klasse“). Die Spezifikation bezieht sich jedoch auf die „Erbschaftsspezifikation“ einer Klasse, anstatt den Begriff „Vererbung“ zu verwenden.

Zunächst weist TypeScript weniger Optionen als C# auf, wenn es um das Definieren von Basisklassen geht. Sie können die Klasse oder die Elemente nicht als nicht überschreibbar, abstrakt oder virtuell deklarieren (auch wenn Schnittstellen einen Großteil der Funktionen bieten, die eine virtuelle Basisklasse bereitstellt).

Es gibt kein Verfahren, mit dem verhindert werden kann, dass einige Elemente nicht vererbt werden. Eine abgeleitete Klasse erbt alle Elemente der Basisklasse einschließlich der öffentlichen und privaten Elemente (alle öffentlichen Elemente der Basisklasse sind überschreibbar, private Elemente hingegen nicht). Definieren Sie zum Überschreiben eines öffentlichen Elements einfach ein Element in der abgeleiteten Klasse mit der gleichen Signatur. Sie können mithilfe des Schlüsselworts „super“ auf eine öffentliche Methode aus einer abgeleiteten Klasse zugreifen. Sie können jedoch nicht auf eine Eigenschaft in der Basisklasse mithilfe von „super“ zugreifen (Sie können die Eigenschaft nur überschreiben).

Mit TypeScript können Sie eine Schnittstelle erweitern, indem Sie einfach eine Schnittstelle mit einem identischen Namen und neuen Elementen deklarieren. Auf diese Weise können Sie vorhandenen JavaScript-Code erweitern, ohne einen neuen benannten Typ erstellen zu müssen. Das Beispiel in Abbildung 5 definiert die Schnittstelle „ICustomerMerge“ durch zwei separate Schnittstellendefinitionen und implementiert die Schnittstelle dann in einer Klasse.

Abbildung 5 – Die über zwei Schnittstellendefinitionen definierte Schnittstelle „ICustomerMerge“

interface ICustomerMerge
{
  MiddleName: string;
}
interface ICustomerMerge
{
  Id: number;
}
class CustomerMerge implements ICustomerMerge
{
  Id: number;
  MiddleName: string;
}

Klassen können ebenfalls andere Klassen erweitern, jedoch keine Schnittstellen. In TypeScript können Schnittstellen auch Klassen erweitern, jedoch nur so, dass Vererbung verwendet wird. Wenn eine Schnittstelle eine Klasse erweitert, enthält die Schnittstelle alle Elemente der Klasse (öffentliche und private), jedoch ohne die Implementierungen der Klasse. In Abbildung 6 weist die Schnittstelle „ICustomer“ das private Element „id“, das öffentliche Element „Id“ und das öffentliche Element „MiddleName“ auf.

Abbildung 6 – Eine erweiterte Klasse mit allen Elementen

class Customer
{
  private id: number;
  get Id(): number
  {
    return this.id
  }
  set Id( value: number )
  {
    this.id = value;
  }
}
interface ICustomer extends Customer
{
  MiddleName: string;
}

Für die Schnittstelle „ICustomer“ gilt eine erhebliche Einschränkung – sie kann nur mit Klassen verwendet werden, die die die gleiche Klasse erweitern, die die Schnittstelle erweitert hat (in diesem Fall ist dies die Klasse „Customer“). TypeScript erfordert, dass Sie private Elemente in die Schnittstelle einschließen, die von der Klasse geerbt werden sollen, die die Schnittstelle erweitert, anstatt diese in der abgeleiteten Klasse erneut zu implementieren. Eine neue Klasse, die die Schnittstelle „ICustomer“ verwendet, müsste z. B. eine Implementierung für „MiddleName“ bereitstellen (weil dieses Element nur in der Schnittstelle angegeben wird). Ein Entwickler, der „ICustomer“ verwendet, kann sich für Vererbung entscheiden oder öffentliche Methoden aus der Klasse „Customer“ überschreiben. Er ist jedoch nicht in der Lage, das private Element „id“ zu überschreiben.

Dieses Beispiel zeigt eine Klasse (namens „NewCustomer“), die die Schnittstelle „ICustomer“ implementiert und die Klasse „Customer“ wie erforderlich erweitert. In diesem Beispiel erbt „NewCustomer“ die Implementierung von „Id“ von „Customer“ und stellt eine Implementierung für „MiddleName“ zur Verfügung:

class NewCustomer extends Customer implements ICustomer
{
  MiddleName: string;
}

Diese Kombination aus Schnittstellen, Klassen, Implementierung und Erweiterung ermöglicht von Ihnen definierten Klassen die kontrollierte Erweiterung von in anderen Klassen definierten Objektmodellen (weitere Einzelheiten finden Sie in Abschnitt 7.3 der Sprachspezifikation „Schnittstellenerweiternde Klassen“). Gekoppelt mit der Möglichkeit von TypeScript, Informationen zu anderen JavaScript-Bibliotheken verwenden zu können, wird ermöglicht, TypeScript-Code zu schreiben, der mit den Objekten funktioniert, die in diesen Bibliotheken definiert sind.

TypeScript kennt Ihre Bibliotheken

TypeScript kennt nicht nur die in Ihrer Anwendung definierten Klassen und Schnittstellen. Sie können TypeScript auch Informationen zu anderen Objektbibliotheken zur Verfügung stellen. Dies geschieht über das TypeScript-Schlüsselwort „declare“. Dieses erstellt die in der Spezifikation beschriebenen „Umgebungsdeklarationen“. Sie müssen das Schlüsselwort „declare“ möglicherweise niemals selbst verwenden, weil Definitionsdateien für die meisten JavaScript-Bibliotheken auf der DefinitelyTyped-Website unter definitelytyped.org verfügbar sind. Durch diese Definitionsdateien kann TypeScript gewissermaßen die Dokumentation zu den Bibliotheken „lesen“, mit denen Sie arbeiten müssen.

Das „Lesen der Dokumentation“ bedeutet natürlich, dass Sie datentypisierte IntelliSense-Unterstützung und Überprüfungen zur Kompilierzeit erhalten, wenn Sie die Objekte verwenden, aus denen die Bibliothek besteht. Außerdem kann TypeScript unter bestimmten Umständen den Typ einer Variablen aus dem Kontext ableiten, in dem sie verwendet wird. Dank der in TypeScript enthaltenen Definitionsdatei „lib.d.ts“ geht TypeScript davon aus, dass der Variablenanker im folgenden Code vom Typ „HTMLAnchorElement“ ist:

var anchor = document.createElement( "a" );

Die Definitionsdatei gibt an, dass dies das von der Methode „createElement“ zurückgegebene Ergebnis ist, wenn die Zeichenfolge „a“ an diese Methode übergeben wird. Weil bekannt ist, dass der Anker ein „HTMLAnchorElement“ ist, bedeutet dies, dass TypeScript weiß, dass die Ankervariable z. B. die Methode „addEvent­Listener“ unterstützt.

Der TypeScript-Datentyp „inference“ funktioniert ebenfalls mit Parametertypen. Beispielsweise akzeptiert die Methode „addEventListener“ zwei Parameter. Der zweite Parameter ist eine Funktion, in der „addEventListener“ ein Objekt vom Typ „PointerEvent“ übergibt. TypeScript weiß dies und unterstützt den Zugriff auf die Eigenschaft „cancelBubble“ der Klasse „PointerEvent“ in der Funktion:

span.addEventListener("pointerenter", function ( e )
{
  e.cancelBubble = true;
}

Auf die gleiche Weise, wie die Datei „lib.d.ts“ Informationen zum HTML-DOM bereitstellt, stellen die Definitionsdateien für andere JavaScript-Bibliotheken eine ähnliche Funktionalität bereit. Nachdem ich z. B. die Datei „backbone.d.ts“ meinem Projekt hinzugefügt habe, kann ich eine Klasse deklarieren, die die Model-Klasse „Backbone“ erweitert und meine eigene Schnittstelle mit dem folgenden Code implementiert:

class CustomerShort extends bb.Model implements ICustomerShort
{
}

Wenn Sie die Einzelheiten der Verwendung von TypeScript mit „Backbone“ und „Knockout“ interessieren, lesen Sie meine Kolumnen zu „TypeScript in der Praxis“ unter bit.ly/1BRh8NJ. Im kommenden Jahr werden ich die Einzelheiten der Verwendung von TypeScript mit Angular beschreiben.

TypeScript kann weit mehr als in diesem Artikel beschrieben. Es ist vorgesehen, dass TypeScript, Version 1.3, Union-Datentypen (z. B. für die Unterstützung von Funktionen, die eine Liste bestimmter Typen zurückgeben) und Tupel enthält. Das TypeScript-Team arbeitet mit anderen Teams daran, Datentypen auf JavaScript (Flow und Angular) anzuwenden, damit sichergestellt ist, dass TypeScript mit möglichst vielen JavaScript-Bibliotheken zusammenarbeiten kann.

Wenn Sie eine Aufgabe ausführen müssen, die von JavaScript, nicht jedoch von TypeScript unterstützt wird, können Sie Ihren JavaScript-Code immer integrieren, weil TypeScript eine Obermenge von JavaScript ist. Die Frage stellt sich also weiterhin – welche dieser Sprachen bevorzugen Sie, um Ihren clientseitigen Code zu schreiben?


Peter Vogel gehört der Geschäftsleitung von PH&V Information Services an und ist auf Webentwicklung mit dem Schwerpunkt SOA, clientseitige Entwicklung und Benutzeroberflächendesign spezialisiert. Kunden von PH&V sind z. B. die Canadian Imperial Bank of Commerce, Volvo und Microsoft. Außerdem unterrichtet er und erstellt Kurse für Learning Tree International und ist Autor der Kolumne „.NET in der Praxis“ für VisualStudioMagazine.com.

Unser Dank gilt dem folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Ryan Cavanaugh