News

TypeScript 3.9 bringt verbessertes Promise-Handling und neue Kommentar-Features

TypeScript. (Screenshot: typescriptlang.org)

Lesezeit: 7 Min.
Artikel merken

Bei TypeScript 3.9 liegt der Fokus klar auf Performanz und Stabilität. Unter den Neuerungen und Bug-Fixes sind neue Kommentarfeatures und verbessertes Promise-Handling; eines der angekündigten Features hat es allerdings nicht zur Veröffentlichung in Version 3.9 geschafft.

TypeScript basiert auf Vorschlägen zum ES6-Standard. Viele Konzepte der Sprache wie Klassen, Vererbung oder Module sind daher deckungsgleich mit ES6. Syntax und Semantik der Sprache sind stark an JavaScript angelehnt. Die Lernkurve ist für Entwickler, die von JavaScript zu TypeScript wechseln, relativ moderat.

Wer mit TypeScript arbeitet, kann dabei bereits existenten JavaScript-Code und die meisten Frameworks und Libraries aus dem JavaScript-Ökosystem nutzen. TypeScript-Code kann über JavaScript aufgerufen werden. Am Ende wird TypeScript zu JavaScript-Code kompiliert, der in allen Browsern, in Node.js und in jeder JavaScript-Engine läuft, die ECMAScript ab Version 3 unterstützt. Version 3.9 ist jetzt als Beta-Release verfügbar. Die meisten Neuerungen zielen auf Performanz und Stabilität sowie eine verbesserte Dev-Experience ab, viele Bug-Fixes und Vorschläge kommen dieses Mal aus der Community.

Verbesserter Inferenzprozess und Promise.all

In TypeScript 3.7 und später wurden Funktionsaufrufe wie Promise.all und Promise.race aktualisiert. Damit gingen allerdings einige Überraschungen einher, vor allem, wenn Values mit null oder undefined im Spiel waren.

interface Lion {
roar(): void
}

interface Seal {
singKissFromARose(): void
}

async function visitZoo(lionExhibit: Promise, sealExhibit: Promise) {
let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]);
lion.roar(); // uh oh
// ~~~~
// Object is possibly 'undefined'.
}

*alle Code-Beispiele wurden aus dem offiziellen Blogpost zum Release übernommen.

Dass SealExhibit ein undefined enthält, geht im Beispiel komischerweise auf Lion über und ihr bekommt die Fehlermeldung Object is possibly undefined. Dieses unerwünschte Verhalten wurde in V 3.9 dank eines Pull-Requests von Jack Bates elimiert, sodass der oben stehende Code jetzt keine Fehler mehr wirft. Wer aufgrund derartiger Probleme mit Promises bei Versionen älter 3.7 geblieben ist, kann mit V 3.9 jetzt auch endlich upgraden.

Fast fertig!

Bitte klicke auf den Link in der Bestätigungsmail, um deine Anmeldung abzuschließen.

Du willst noch weitere Infos zum Newsletter? Jetzt mehr erfahren

Verbesserte Performanz

TypeScript 3.9 bringt einige Performanz-Upgrades. In vorherigen Versionen gab es diesbezüglich wohl Probleme im Zusammenhang mit Packages wie zum Beispiel material-ui oder styled-components. Über Pull-Requests konnten laut dem offiziellen Blogpost zum Release Probleme mit großen unions, Intersections, conditionals und mapped-Typen behoben werden und so pro Pull-Request eine Reduzierung der Kompilierzeit von fünf bis zehn Prozent erreicht werden. Material-UIs Compile-Time zum Beispiel ist damit in V 3.9 um etwa 40 Prozent reduziert.

Auch was den Editor angeht, kommt TS mit einigen Performanz-Verbesserungen. Beim Umbenennen einer Datei konnte es wohl bei der Arbeit mit bisherigen TS-Versionen passieren, dass VS Code bis zu zehn Sekunden dafür brauchte, herauszufinden, welche import-Statements aktualisiert werden müssen. In V 3.9 wurde geändert, wie Datei-Lookups von Compiler und Language-Services gecached werden, was zu einer Verbesserung der Dev-Experience für TypeScript-Entwickler und -Entwicklerinnen führen sollte.

// @ts-expect-error-Kommentare

TypeScript 3.9 bringt ein neues Feature namens @ts-expect-error-Kommentare. Nützlich ist das zum Beispiel, wenn ihr eine Library in TypeScript schreibt. Als Teil eures öffentlichen API exportiert ihr eine Funktion namens doStuff. Um Type-Checking-Errors für TypeScript-Entwicklerinnen zu ermöglichen, akzeptiert die Funktion zwei Strings als Parameter. Außerdem wird ein Laufzeitcheck durchgeführt, um auch JS-Entwicklern eine hilfreiche Fehlermeldung bieten zu können.

function doStuff(abc: string, xyz: string) {
assert(typeof abc === "string");
assert(typeof xyz === "string");

// do some stuff
}


Bei falscher Verwendung dieser Funktion bekommen TypeScript-User also ein hilfreiches kleines rotes Rechteck und JS-Nutzer einen Assertion Error. Um dieses Verhalten zu testen, schreibt ihr einen Unit-Test:
expect(() => {
doStuff(123, 456);
}).toThrow();

Schreibt ihr den Unit-Test jedoch in TypeScript, bekommt ihr folgende Fehlermeldung:

doStuff(123, 456);
// ~~~
// error: Type 'number' is not assignable to type 'string'.

Der @ts-expect-error-Kommentar wurde in TypeScript 3.9 eingeführt, um genau das zu vermeiden. Den Kommentar könnt ihr einer Zeile voranstellen. Das bewirkt, dass ein TS-Error unterdrückt, das heißt, nicht angezeigt wird, falls es andernfalls einen gegeben hätte. Wenn nicht, führt das neue Feature dazu, dass TypeScript euch sagt, dass sein Einsatz nicht nötig gewesen wäre.
Um das zu veranschaulichen: Dieser Code wird ohne Fehlermeldung ausgeführt,

// @ts-expect-error
console.log(47 * "octopus");

während folgender Code:

// @ts-expect-error
console.log(1 + 1);

zu dieser Fehlermeldung führt:
Unused '@ts-expect-error' directive.
Alle Details dazu könnt ihr im zugehörigen Pull-Request nachlesen.

Wer sich jetzt fragt, wie sich @ts-ignore und @ts-expect-error unterscheiden: Der Unterschied ist, dass @ts-ignore Kommentare nichts tun, wenn die betreffende Zeile korrekt ist. Welchen ihr nutzt, ist abhängig von einigen Faktoren, eine Entscheidungshilfe findet ihr im offiziellen Blogpost zum Release.

Jetzt auch für Ternary Conditionals: Uncalled Function Checks

In TypeScript 3.7 wurde mit uncalled Function Checks ein Feature eingeführt, das dazu führt, dass ihr eine Fehlermeldung bekommt, wenn ihr den Funktionsaufruf vergessen habt.

function hasImportantPermissions(): boolean {
// ...
}

// Oops!
if (hasImportantPermissions) {
// ~~~~~~~~~~~~~~~~~~~~~~~
// This condition will always return true since the function is always defined.
// Did you mean to call it instead?
deleteAllTheImportantFiles();
}

Bislang war die Fehlermeldung allerdings Bedingungen in if-Statements vorbehalten, jetzt wird sie auch in Ternary Conditionals unterstützt – gemeint ist folgende Schreibweise:
cond ? trueExpr : falseExpr;

Breaking Changes

Wie jedes Major Release kommt auch die neue TypeScript-Version mit einigen Breaking Changes.

Parsing-Unterschiede in Optional Chaining und Non-Null Assertions

Eine davon den erst kürzlich eingeführten Optional-Chaining Operator ?. Kombiniert mit dem nicht-Null Assertion-Operatior ! führte er zu einem Verhalten, das Entwickler als kontraintuitiv empfanden.
Folgendes Beispiel
foo?.bar!.baz
wurde interpretiert wie der folgende JS-Code:
(foo?.bar).baz

Das Problem: Die Klammern stoppen das erwünschte Kurzschluss-Verhalten des Optional Chaining, so dass im Fall von foo = undefined, der Zugriff auf baz zu einem Laufzeitfehler führte. Der Hinweis auf dieses Verhalten kam aus dem Babel-Team. Die meisten waren der Meinung, dass der !-Operator einfach weggelassen werden sollte, das Codesnippet also als foo?.bar.baz interpretiert werden soll. Ist foo hier undefined, evaluiert folglich die ganze Funktion einfach zu undefined.

Das ist zwar ein Breaking Change, allerdings einer, bei dem davon auszugehen ist, dass viele Entwickler ihren Code mit der neuen Interpretation im Hinterkopf geschrieben haben. Wer das frühere Verhalten weiter nutzen will, erreicht das über Klammern um alles, was sich links des !-Operators befindet:
(foo?.bar)!.baz

} und > sind jetzt keine validen JSX-Text-Zeichen mehr

In JSX ist die Verwendung der Zeichen } und > an Textpositionen nicht erlaubt. TypeScript und Babel gleichen sich dieser Regel an, die neue Art der Notation sieht vor, eine HTML-Escape-Sequenz oder sogenannte Literal Strings zu nutzen. Zudem bekommt ihr dann auch gleich eine nützliche Fehlermeldung, zum Beispiel so:

let directions =
Navigate to: Menu Bar > Tools > Options

// ~ ~
// Unexpected token. Did you mean `{'>'}` or `>`?

Praktischerweise kommt der vorgeschlagene Quickfix mit einer „Fix all in File“-Option, für den Fall, dass der Fehler an mehreren Stellen innerhalb einer Datei auftaucht.

Striktere Checks für Intersections und Optional Properties

Es ist grundsätzlich möglich, Intersection-Types wie A & B C zuzuweisen, manchmal kommt es dabei allerdings zu Problemen mit sogenannten Optional Properties. Der folgende Code:

interface A {
a: number; // notice this is 'number'
}

interface B {
b: string;
}

interface C {
a?: boolean; // notice this is 'boolean'
b: string;
}

declare let x: A & B;
declare let y: C;

y = x;


war in früheren TypeScript-Versionen erlaubt. Zwar ist A nicht mit C kompatibel, B aber schon, das reichte bislang aus. Das ändert sich mit TypeScript 3.9: Ist jeder Typ in einer Intersection ein konkreter Objekttyp, betrachtet das Typsystem alle Properties auf einmal. Im Fall des oben stehenden Codes würde es also bemerken, dass die Property a von A & B nicht mit der von C kompatibel ist und folgenden Fehler werfen:
Type 'A & B' is not assignable to type 'C'.
Types of property 'a' are incompatible.
Type 'number' is not assignable to type 'boolean | undefined'.

Intersections Reduced By Discriminant Properties

Es gibt Fälle, bei denen Types Values beschreiben, die gar nicht existieren:

declare function smushObjects<T, U>(x: T, y: U): T & U;

interface Circle {
kind: "circle";
radius: number;
}

interface Square {
kind: "square";
sideLength: number;
}

declare let x: Circle;
declare let y: Square;

let z = smushObjects(x, y);
console.log(z.kind);

Komisch an diesem Code-Snippet ist, dass es nunmal keinen Weg gibt, eine Intersection von Circle und Square herzustellen, weil deren kind-fields einfach nicht kompatibel sind. Bisher wäre dieser Code aber so durchgegangen – der Type von kind war dabei never, einfach weil “circle” & “square” Values beschreibt, die es nie geben könnte. In TypeScript 3.9 gibt es dafür striktere Regeln. Eine Intersection von Circle und Square wird vom Typsystem aufgrund der inkompatiblen kind-Properties als nicht möglich erkannt. Anstatt dass jetzt der Type von z.kind zu never wird, wird jetzt der type von z (also Circle & Square) selbst zu never und ihr bekommt folgende Fehlermeldung:
Property 'kind' does not exist on type 'never'.

Fälle, in denen die Neuerung sich als nicht abwärtskompatibel herausstellte, betrafen laut dem offiziellen Blogpost zum Release Code mit – wie im Beispiel – leicht fehlerhaften Typenangaben.

Getter und Setter sind nicht mehr numerierbar

Bisher wurden get und set Accessoren in Klassen so ausgegeben, dass sie numerierbar waren. Die ECMAScript-Spezifikation sieht aber vor, dass sie das nicht sind. Je nach Target konnte sich das Verhalten eures TypeScript-Codes demnach unterscheiden. Dank eines externen Pull-Requests ist TypeScript mit Version 3.9 näher an der ES-Spezifikation.

Verändertes Verhalten für Typenparameter, die any extenden

Bisher konnte ein auf any beschränkter Typenparameter äquivalent zu any gehandhabt werden.

function foo(arg: T) {
arg.spfjgerijghoied; // no error!
}

Dabei handelte es sich aber offenbar um ein Versehen, das mit V 3.9 aus der Welt geschafft wird:
function foo(arg: T) {
arg.spfjgerijghoied;
// ~~~~~~~~~~~~~~~
// Property 'spfjgerijghoied' does not exist on type 'T'.
}

export * wird ab V 3.9 immer mit ausgegeben

Bisher wurden Deklarationen wie export * from “foo” im ausgegebenen JS weggelassen, wenn foo keine Values exportiert. Das Problem dabei: Diese typengesteuerte Art der Ausgabe kann von Babel nicht emuliert werden. Mit der neuen Version werden export *-Deklarationen deshalb einfach immer ausgegeben.

Neue Editor-Features

Außerdem kommt TypeScript 3.9 mit einigen neuen Editor-Features, darunter auto-Importe von CommonJS-Modulen und Unterstützung for solution-file-ähnliche ts-config.json-Dateien. Alle weiteren Neuerungen und Details erfahrt ihr über den offiziellen Blogpost zum Release .

Und der awaited-Operator?

Wer den TypeScript-Issue-Tracker oder die Design-Meeting-Notes des Projekts verfolgt hat, kennt und erwartet den neuen awaited-Operator vielleicht schon. Er soll es ermöglichen, Promise-Unwrapping in JS zukünftig genau modellieren zu können. Eine Veröffentlichung des Features war zwar für V 3.9 geplant, bei der Arbeit daran kam allerdings die Erkenntnis, dass das Feature zum jetzigen Zeitpunkt noch nicht ausgereift genug ist. Aus diesem Grund wurde der Operator vorerst aus dem Haupt-Branch entfernt und seine Veröffentlichung auf eine spätere Version verschoben.

Mehr zum Thema: 

Das könnte dich auch interessieren

Bitte beachte unsere Community-Richtlinien

Wir freuen uns über kontroverse Diskussionen, die gerne auch mal hitzig geführt werden dürfen. Beleidigende, grob anstößige, rassistische und strafrechtlich relevante Äußerungen und Beiträge tolerieren wir nicht. Bitte achte darauf, dass du keine Texte veröffentlichst, für die du keine ausdrückliche Erlaubnis des Urhebers hast. Ebenfalls nicht erlaubt ist der Missbrauch der Webangebote unter t3n.de als Werbeplattform. Die Nennung von Produktnamen, Herstellern, Dienstleistern und Websites ist nur dann zulässig, wenn damit nicht vorrangig der Zweck der Werbung verfolgt wird. Wir behalten uns vor, Beiträge, die diese Regeln verletzen, zu löschen und Accounts zeitweilig oder auf Dauer zu sperren.

Trotz all dieser notwendigen Regeln: Diskutiere kontrovers, sage anderen deine Meinung, trage mit weiterführenden Informationen zum Wissensaustausch bei, aber bleibe dabei fair und respektiere die Meinung anderer. Wir wünschen Dir viel Spaß mit den Webangeboten von t3n und freuen uns auf spannende Beiträge.

Dein t3n-Team

Schreib den ersten Kommentar!

Melde dich mit deinem t3n Account an oder fülle die unteren Felder aus.

Bitte schalte deinen Adblocker für t3n.de aus!

Hallo und herzlich willkommen bei t3n!

Bitte schalte deinen Adblocker für t3n.de aus, um diesen Artikel zu lesen.

Wir sind ein unabhängiger Publisher mit einem Team von mehr als 75 fantastischen Menschen, aber ohne riesigen Konzern im Rücken. Banner und ähnliche Werbemittel sind für unsere Finanzierung sehr wichtig.

Schon jetzt und im Namen der gesamten t3n-Crew: vielen Dank für deine Unterstützung! 🙌

Digitales High Five
Holger Schellkopf (Chefredakteur t3n)

Anleitung zur Deaktivierung

Artikel merken

Bitte melde dich an, um diesen Artikel in deiner persönlichen Merkliste auf t3n zu speichern.

Jetzt registrieren und merken

Du hast schon einen t3n-Account? Hier anmelden

oder