Fußnoten

Vorwort

1Ein JavaScript-Beispiel: Angenommen, Sie haben ein Objekt o, dem die Eigenschaft k mit dem Wert 3 hinzugefügt werden soll. Dann können Sie o entweder direkt verändern, indem Sie schreiben o.k = 3, oder Sie können die Änderungen durch die Erstellung eines neuen Objekts durchführen: let p = {...o, k: 3}.

1 Einführung

1Je nachdem, welche statisch typisierte Sprache Sie benutzen, kann »ungültig« eine Reihe von Dingen bedeuten. Das können Programme sein, die bei der Ausführung abstürzen, bis hin zu Situationen, in denen nichts abstürzt, aber trotzdem unsinnige Dinge passieren.

2Keine Sorge, wenn der Begriff »Typebene« für Sie noch nicht klar ist. Wir werden ihn in späteren Kapiteln genauer erklären.

2 TypeScript aus der Vogelperspektive

1In diesem Bereich gibt es viele verschiedene Sprachen: JavaScript, Python und Ruby leiten die Typen zur Laufzeit ab; Haskell und OCaml überprüfen auf fehlende Typen bzw. leiten die Typen während der Kompilierung ab; Scala und TypeScript benötigen einige explizite Typen. Der Rest wird während der Kompilierung abgeleitet und überprüft. Java und C erfordern explizite Annotationen für fast alles, wobei die Überprüfung während der Kompilierung stattfindet.

2Nur um das klarzustellen: Einige Syntaxfehler werden von JavaScript bereits nach dem Parsen, aber vor der Ausführung des Programms ausgelöst. Wenn Sie JavaScript als Teil eines Build-Prozesses (zum Beispiel mit Babel) parsen, können Sie diese Fehler bereits während der Build-Phase zum Vorschein bringen.

3Inkrementell kompilierte Sprachen können bei kleinen Änderungen schnell rekompiliert werden, ohne dass hierfür das gesamte Programm (inklusive der Teile, die Sie nicht verändert haben) neu kompiliert werden muss.

4Damit gehört TSC zur mystischen Klasse der sogenannten Self-Hosting Compiler, d.h. Compiler, die sich selbst kompilieren.

5Für diese Übungen erzeugen wir tsconfig.json manuell. Wenn Sie in Zukunft ein TypeScript-Projekt erstellen, können Sie den TSC-eigenen Initialisierungsbefehl ./node_modules/.bin/tsc --init verwenden, um die Datei automatisch erstellen zu lassen.

3 Alles über Typen

1Fast. Wenn unknown Teil eines Vereinigungstyps ist, ist das Ergebnis der Vereinigung ebenfalls unknown. Mehr zu Vereinigungstypen finden Sie unter »Vereinigungs- und Schnittmengentypen« auf Seite 32.

2JavaScript-Objekte verwenden Strings als Schlüssel; Arrays sind besondere Objekte, die numerische Schlüssel verwenden.

3Dabei gibt es nur einen kleinen technischen Unterschied: Mit {} können Sie beliebige Typen für die eingebauten Methoden des Object-Prototyps definieren, wie .toString und .hasOwnProperty (mehr Informationen zu Prototypen finden Sie im MDN (https://mzl.la/2VSuDJz)), während Object erzwingt, dass die von Ihnen deklarierten Typen denen des Object-Prototypen zuweisbar sind.

Dieser Code führt zum Beispiel einen Typecheck aus: let a: {} = {toString() { return 3 }}. Wenn Sie die Typannotation in Object ändern, beschwert sich TypeScript: let b: Object = {toString() { return 3 }} ergibt Error TS2322: Type 'number' is not assignable to type 'string'.

4Das Akronym DRY steht für »Don’t Repeat Yourself« – »Wiederholen Sie sich nicht«. Man soll vermeiden, den gleichen Code mehrmals zu schreiben. Es wurde von Andrew Hunt und David Thomas in Ihrem Buch The Pragmatic Programmer: From Journeyman to Master (Addison-Wesley) eingeführt.

5Springen Sie zum Abschnitt »Unterscheidung von Vereinigungstypen« auf Seite 130, um zu lernen, wie Sie TypeScript mitteilen können, dass Ihre Vereinigungsmenge entkoppelt (disjoint) ist und der Typ der Vereinigungsmenge entweder der eine oder der andere, aber nicht beides sein kann.

6Den bottom type kann man sich als Typ ohne Werte vorstellen. Ein bottom type entspricht einer mathematischen Aussage, die immer falsch ist.

4 Funktionen

1Warum sind sie unsicher? Wenn Sie das letzte Beispiel in Ihren Codeeditor eingeben, werden Sie sehen, dass der Typ Function lautet. Das heißt, Function ist ein aufrufbares Objekt (durch die nachgestellten runden Klammern ()), das alle Prototyp-Methoden von Function.prototype übernimmt. Seine Parameter und Rückgabewerte sind aber nicht typisiert. Sie können die Funktion also mit beliebigen Argumenten aufrufen, während TypeScript ungerührt danebensteht und Ihnen zusieht, wie Sie etwas tun, das, ganz gleich in welcher Stadt Sie leben, mit Sicherheit überaus illegal ist.

2Einen umfassenden Einblick zu this erhalten Sie in der You Don’t Know JS (http://shop.oreilly.com/product/0636920033738.do)-Serie von Kyle Simpson (erhältlich bei O’Reilly).

3Object und Number sind übrigens keine Iteratoren.

4Die Ausnahmen dieser Faustregel sind Enums und Namensräume. Enums erzeugen sowohl einen Typ und einen Wert, während Namensräume ausschließlich auf Wertebene existieren. Eine vollständige Referenz finden Sie unter Anhang C.

5Falls Sie den Begriff »Callback« noch nicht kennen: Das ist einfach eine Funktion, die als Argument an eine andere Funktion übergeben wird.

6Mehr dazu finden Sie übrigens unter »Typverfeinerung (refinement)« auf Seite 128.

7Zumindest fast. TypeScript gibt literalen Überladungen gegenüber nicht-literalen den Vorzug, bevor sie der Reihe nach aufgelöst werden. Sie sollten sich auf dieses Merkmal aber nicht verlassen, weil Ihre Überladungen dadurch für andere Programmierer, die dieses Verhalten vielleicht nicht kennen, schwer verständlich werden.

8Um unsere Implementierung etwas zu vereinfachen, wird in unserer call-Version die mögliche Übergabe von this nicht berücksichtigt.

9Es gibt ein paar Programmiersprachen (zum Beispiel die Haskell-artige Sprache Idris), die eingebaute Mechanismen zum Auflösen von Beschränkungen besitzen, die aus den von Ihnen geschriebenen Funktionssignaturen automatisch Funktionskörper implementieren können!

5 Klassen und Interfaces

1Oder ihre Unterstützung durch JavaScript-Klassen ist für eine baldige Version vorgesehen.

2Da TypeScript strukturell typisiert ist, ist die Beziehung für Klassen eher eine »SIEHT-AUS-WIE«-Beziehung. Jedes Objekt, das die gleiche Form wie seine Klasse implementiert, ist auf diesen Typ seiner Klasse zuweisbar.

3Aber Vorsicht! Hier ist TypeScript ziemlich pingelig: Der Typ der Argumente des Konstruktor-Typs muss any[] sein (nicht void[], unknown[] etc.), um ihn erweitern zu können.

4Eine Handvoll Sprachen – darunter Scala, PHP, Kotlin und Rust – implementieren eine abgespeckte Version der Mixins, die als Traits bezeichnet werden. Traits funktionieren wie Mixins, besitzen aber keine Konstruktoren und unterstützen keine Instanzeigenschaften. Dadurch ist es einfacher, sie einzubinden und Kollisionen zwischen mehreren Traits zu verhindern, die auf Zustände zugreifen, die von ihnen und der Basisklasse gemeinsam genutzt werden.

6 Fortgeschrittene Typen

1Die symbolische Ausführung ist eine Form der Programmanalyse, bei der ein spezielles Programm namens symbolischer Evaluator benutzt wird, um Ihr Programm auf die gleiche Weise wie eine Laufzeitumgebung auszuführen. Dabei werden allerdings keine definitiven Werte und Variablen zugewiesen. Stattdessen wird jede Variable als Symbol modelliert, dessen Wert beim Ausführen des Programms beschränkt wird. Durch die symbolische Ausführung lassen sich Dinge sagen wie: »Diese Variable wird nie benutzt.« oder »Diese Funktion kehrt nie zurück.« oder »Im positiven Zweig der if-Anweisung in Zeile 102 hat die Variable x garantiert nicht den Wert null

2Die flussbasierte Typableitung wird von einer Handvoll Sprachen unterstützt, wie beispielsweise TypeScript, Flow, Kotlin und Ceylon. Die flussbasierte Typableitung ist eine Möglichkeit, die Typen in einem Codeblock zu verfeinern, und stellt eine Alternative zur expliziten Typannotation, wie in C und Java, und der Mustererkennung aus Sprachen wie Haskell, OCaml und Scala dar. Die Idee ist, eine symbolische Ausführungs-Engine direkt in den Typechecker zu integrieren, um diesem Rückmeldungen zu geben und ein Programm so zu analysieren, wie es auch ein Programmierer tun würde.

3JavaScript besitzt sieben Werte, die nicht-strikt falsch (falsy) sind: null, undefined, NaN, 0, -0, "" und natürlich false. Alles andere ist nicht-strikt wahr (truthy).

4DefinitelyTyped (https://github.com/DefinitelyTyped/DefinitelyTyped) ist das Open-Source-Repository für Typdeklarationen von Drittanbieter-JavaScript. Mehr hierzu finden Sie unter »JavaScript, für das es Typdeklarationen auf DefinitelyTyped gibt« auf Seite 249.

5In manchen Sprachen werden sie auch als opake Typen bezeichnet.

6Daneben gibt es noch weitere Gründe, Prototypen nicht zu erweitern. Hierzu gehören Code-Portabilität, die Verwendung expliziterer Abhängigkeits-Graphen oder die Steigerung der Performance, indem nur die tatsächlich benötigten Methoden geladen werden. Sicherheit ist jedenfalls kein Grund mehr.

7 Fehlerbehandlung

1Ein Standard für das Einbetten von Dokumentation direkt in den dokumentierten Code.

2Falls Sie noch nicht mit Java gearbeitet haben: Eine throws-Klausel gibt an, welche Arten von Laufzeit-Ausnahmen eine Methode auslösen kann, damit klar ist, welche Ausnahmen der aufrufende Code selbst behandeln muss.

3Auch als Maybe-Typ bezeichnet.

4Googlen Sie nach »try type« oder »either type« um weitere Informationen zu diesen Typen zu erhalten.

8 Asynchrone Programmierung, Nebenläufigkeit und Parallelismus

1Na gut. Grundsätzlich geht es schon. Allerdings müssen Sie dafür einen Fork Ihrer Browser-Plattform erstellen und eine C++-Erweiterung für NodeJS schreiben.

2Aufmerksame Leser werden bemerkt haben, dass diese API große Ähnlichkeit mit der in »Der Option-Typ« auf Seite 167 entwickelten flatMap-API hat. Das ist kein Zufall! Sowohl Promise als auch Option sind vom Monad-Entwurfsmuster inspiriert, das durch die funktionale Programmiersprache Haskell bekannt geworden ist.

3Observables sind in der reaktiven Programmierung ein Hauptbestandteil, Dinge über einen bestimmten Zeitraum zu verarbeiten. Ein Vorschlag zur Standardisierung von Observables ist in Arbeit und kann unter https://tc39.github.io/proposal-observable/ öffentlich eingesehen werden. Sobald dieser Vorschlag eine größere Unterstützung durch die JavaScript-Engines erhält, werden wir in zukünftigen Versionen dieses Buchs natürlich darauf eingehen.

4Außer Funktionen, Fehlern, DOM-Knoten, Eigenschaften-Deskriptoren, Getter, Setter sowie Prototyp-Methoden und -eigenschaften. Weitere Informationen hierzu finden Sie in der HTML5-Spezifikation (http://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data).

5Um bestimmte Daten (z.B. ArrayBuffer) zwischen Threads per Referenz zu übergeben, können Sie außerdem die Transferable-API verwenden. In diesem Abschnitt setzen wir Transferable nicht ein, um die Objekt-Eigentümerschaft zwischen Threads explizit zu übertragen. Das ist aber nur ein Implementierungsdetail. Wenn Sie Transferable verwenden, ist der Ansatz aus Sicht der Typsicherheit identisch.

6Diese Implementierung ist naiv, weil für jeden ausgegebenen Befehl ein neuer Worker erzeugt wird. Im wahren Leben verwenden Sie wahrscheinlich eher einen Pooling-Mechanismus, der eine Reihe frischer Workers bereithält und nicht mehr Gebrauchte wieder freigibt.

10 Namensräume.Module

1Ich hoffe inständig, dass dieser Witz in Würde altert und ich es nicht bereue, nicht in Bitcoin investiert zu haben.

11 Zusammenarbeit mit JavaScript

1Das Wildcard-Matching mit * folgt den gleichen Regeln wie die normale Mustererkennung per glob. (https://en.wikipedia.org/wiki/Glob_(programming))

2DefinitelyTyped ist das Open-Source-Repository für JavaScript-Typdeklarationen. Lesen Sie weiter, um mehr zu erfahren.

3Für wirklich große Projekte kann es ziemlich langsam sein, jede einzelne Datei mit TSC zu verarbeiten. Eine Möglichkeit, die Performance für große Projekte zu steigern, finden Sie unter »Projektreferenzen« auf Seite 260.

4Streng genommen stimmt das nur für Dateien im Modulmodus, aber nicht im Skriptmodus. Mehr hierzu finden Sie unter »Modulmodus oder Skriptmodus« auf Seite 224.

12 TypeScript-Projekte erstellen und ausführen

1Wenn Sie ein Sprachmerkmal verwenden, das TSC nicht transkompiliert und das auch nicht von Ihrer Zielplattform unterstützt wird, gibt es oft ein Babel-Plug-in, dass die Transkompilierung für Sie übernehmen kann. Um das aktuellste Plug-in zu finden, verwenden Sie die Suchmaschine Ihrer Wahl mit dem Suchbegriff »babel plugin <Name des Merkmals>«.