Ankündigung von TypeScript 3.4 RC

Vor einigen Tagen haben wir die Verfügbarkeit unseres Release Candidate (RC) von TypeScript 3.4 angekündigt. Wir hoffen, Feedback und frühe Ausgaben zu sammeln, um sicherzustellen, dass unsere endgültige Version einfach zu erfassen und sofort zu verwenden ist.


Um mit der Verwendung des RC zu beginnen, können Sie ihn über NuGet herunterladen oder npm mit dem folgenden Befehl verwenden:


npm install -g typescript@rc 

Sie können auch Editor-Unterstützung von erhalten



Lassen Sie uns untersuchen, was in 3.4 neu ist!




Dieser Artikel in unserem Blog.

Schnellere nachfolgende Builds mit dem Flag --incremental


Da TypeScript-Dateien kompiliert werden, wird ein Zwischenschritt zwischen dem Schreiben und Ausführen Ihres Codes eingeführt. Eines unserer Ziele ist es, die Bauzeit bei Änderungen an Ihrem Programm zu minimieren. Eine Möglichkeit, dies zu tun, besteht darin, TypeScript im --watch Modus --watch . Wenn sich eine Datei im --watch Modus ändert, kann TypeScript --watch des zuvor erstellten Abhängigkeitsdiagramms Ihres Projekts ermitteln, welche Dateien möglicherweise betroffen sind und erneut überprüft und möglicherweise erneut ausgegeben werden müssen. Dies kann eine vollständige Typprüfung und erneute Ausgabe vermeiden, was kostspielig sein kann.


Es ist jedoch unrealistisch zu erwarten, dass alle Benutzer einen tsc --watch Prozess über Nacht tsc --watch , um morgen früh schnellere Builds zu erhalten. Was ist mit kalten Builds? In den letzten Monaten haben wir daran gearbeitet, ob es eine Möglichkeit gibt, die entsprechenden Informationen aus dem --watch Modus in einer Datei zu speichern und von Build zu Build zu verwenden.


TypeScript 3.4 führt ein neues Flag namens --incremental das TypeScript --incremental , Informationen über das Projektdiagramm aus der letzten Kompilierung zu speichern. --incremental TypeScript das nächste Mal mit --incremental aufgerufen --incremental , werden diese Informationen verwendet, um die kostengünstigste Methode zur Typprüfung und Ausgabe von Änderungen an Ihrem Projekt zu ermitteln.


 // tsconfig.json { "compilerOptions": { "incremental": true, "outDir": "./lib" }, "include": ["./src"] } 

Standardmäßig tsc TypeScript bei diesen Einstellungen beim Ausführen von .tsbuildinfo in unserem Ausgabeverzeichnis ( ./lib ) nach einer Datei mit dem Namen .tsbuildinfo . Wenn ./lib/.tsbuildinfo nicht vorhanden ist, wird es generiert. In diesem Fall versucht tsc , diese Datei zu verwenden, um unsere Ausgabedateien schrittweise zu überprüfen und zu aktualisieren.


Diese .tsbuildinfo Dateien können sicher gelöscht werden und haben zur Laufzeit keine Auswirkungen auf unseren Code. Sie dienen lediglich dazu, die Kompilierung zu beschleunigen. Wir können sie auch beliebig --tsBuildInfoFile und sie mit dem Flag --tsBuildInfoFile beliebigen Stelle --tsBuildInfoFile .


 // front-end.tsconfig.json { "compilerOptions": { "incremental": true, "tsBuildInfoFile": "./buildcache/front-end", "outDir": "./lib" }, "include": ["./src"] } 

Solange niemand anderes versucht, in dieselbe Cache-Datei zu schreiben, sollten wir in der Lage sein, schnellere inkrementelle Cold-Builds zu genießen.


Zusammengesetzte Projekte


Ein Teil der Absicht bei zusammengesetzten Projekten ( tsconfig.json s mit zusammengesetztem composite auf true ) besteht darin, dass Referenzen zwischen verschiedenen Projekten inkrementell erstellt werden können. Daher erstellen zusammengesetzte Projekte immer .tsbuildinfo Dateien.


outFile


Wenn outFile verwendet wird, basiert der Name der Build-Informationsdatei auf dem Namen der Ausgabedatei. Wenn unsere JavaScript-Ausgabedatei beispielsweise ./output/foo.js --incremental , generiert TypeScript unter dem Flag --incremental die Datei ./output/foo.tsbuildinfo . Wie oben kann dies mit dem Flag --tsBuildInfoFile gesteuert werden.


Das --incremental Dateiformat und die Versionierung


Während die von --incremental generierte Datei JSON ist, --incremental die Datei nicht von einem anderen Tool verwendet werden. Wir können keine Garantie für die Stabilität des Inhalts geben. Unsere derzeitige Richtlinie lautet, dass eine Version von TypeScript .tsbuildinfo Dateien, die aus einer anderen Version generiert wurden, nicht versteht.


Verbesserungen für ReadonlyArray und readonly Tupel


TypeScript 3.4 erleichtert die Verwendung von schreibgeschützten Array-ähnlichen Typen ein wenig.


Eine neue Syntax für ReadonlyArray


Der ReadonlyArray Typ beschreibt Array , aus denen nur gelesen werden kann. Jede Variable mit einem Handle für ein ReadonlyArray kann keine Elemente des Arrays hinzufügen, entfernen oder ersetzen.


 function foo(arr: ReadonlyArray<string>) { arr.slice(); // okay arr.push("hello!"); // error! } 

Während es oft eine gute Praxis ist, ReadonlyArray über Array zum Zweck der Absicht zu verwenden, war es oft ein Schmerz, da Arrays eine schönere Syntax haben. Insbesondere ist number[] eine Kurzversion von Array<number> , ebenso wie Date[] eine Kurzform für Array<Date> .


TypeScript 3.4 führt eine neue Syntax für ReadonlyArray , die einen neuen readonly Modifikator für Array-Typen verwendet.


 function foo(arr: readonly string[]) { arr.slice(); // okay arr.push("hello!"); // error! } 

readonly Tupel


TypeScript 3.4 readonly außerdem eine neue Unterstützung für readonly Tupel. Wir können jedem readonly Schlüsselwort readonly , um es zu einem readonly Tupel zu machen, ähnlich wie wir es jetzt mit der Array-Kurzschrift-Syntax tun können. Wie zu erwarten ist, erlauben readonly Tupel im Gegensatz zu normalen Tupeln, in die Slots geschrieben werden können, nur das Lesen von diesen Positionen aus.


 function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error } 

Genauso wie gewöhnliche Tupel Typen sind, die sich von Array aus erstrecken - ein Tupel mit Elementen vom Typ T 1 , T 2 , ... T n erstreckt sich von Array< T 1 | T 2 | ... T n > - readonly Tupel sind Typen, die sich von ReadonlyArray . Ein readonly Tupel mit den Elementen T 1 , T 2 , ... T n erstreckt sich also von ReadonlyArray< T 1 | T 2 | ... T n > .


readonly zugeordnete readonly und readonly Arrays


In früheren Versionen von TypeScript haben wir zugeordnete Typen so verallgemeinert, dass sie bei Array-ähnlichen Typen unterschiedlich funktionieren. Dies bedeutete, dass ein zugeordneter Typ wie Boxify auf Arrays und Tupeln gleichermaßen Boxify konnte.


 interface Box<T> { value: T } type Boxify<T> = { [K in keyof T]: Box<T[K]> } // { a: Box<string>, b: Box<number> } type A = Boxify<{ a: string, b: number }>; // Array<Box<number>> type B = Boxify<number[]>; // [Box<string>, Box<number>] type C = Boxify<[string, boolean]>; 

Leider waren zugeordnete Typen wie der Dienstprogrammtyp Readonly und Readonly keine Readonly .


 // lib.d.ts type Readonly<T> = { readonly [K in keyof T]: T[K] } // How code acted *before* TypeScript 3.4 // { readonly a: string, readonly b: number } type A = Readonly<{ a: string, b: number }>; // number[] type B = Readonly<number[]>; // [string, boolean] type C = Readonly<[string, boolean]>; 

In TypeScript 3.4 readonly der readonly Modifikator in einem zugeordneten Typ automatisch Array-ähnliche Typen in die entsprechenden readonly Gegenstücke.


 // How code acts now *with* TypeScript 3.4 // { readonly a: string, readonly b: number } type A = Readonly<{ a: string, b: number }>; // readonly number[] type B = Readonly<number[]>; // readonly [string, boolean] type C = Readonly<[string, boolean]>; 

In ähnlicher Weise könnten Sie einen Dienstprogrammtyp wie den beschreibbaren zugeordneten Typ schreiben, der die readonly entfernt und readonly Array-Container wieder in ihre veränderlichen Entsprechungen konvertiert.


 type Writable<T> = { -readonly [K in keyof T]: T[K] } // { a: string, b: number } type A = Writable<{ readonly a: string; readonly b: number }>; // number[] type B = Writable<readonly number[]>; // [string, boolean] type C = Writable<readonly [string, boolean]>; 

Vorsichtsmaßnahmen


Trotz seines Erscheinungsbilds kann der readonly nur für die Syntax von readonly und readonly verwendet werden. Es ist kein Allzweckoperator.


 let err1: readonly Set<number>; // error! let err2: readonly Array<boolean>; // error! let okay: readonly boolean[]; // works fine 

const Behauptungen


Beim Deklarieren einer veränderlichen Variablen oder Eigenschaft erweitert TypeScript häufig die Werte, um sicherzustellen, dass wir später Dinge zuweisen können, ohne einen expliziten Typ zu schreiben.


 let x = "hello"; // hurray! we can assign to 'x' later on! x = "world"; 

Technisch gesehen hat jeder Literalwert einen Literaltyp. Oben wurde der Typ "hello" auf die Typzeichenfolge erweitert, bevor auf einen Typ für x .


Eine alternative Sichtweise könnte sein, zu sagen, dass x den ursprünglichen Literaltyp "hello" und dass wir später nicht "world" wie "hello" zuweisen können:


 let x: "hello" = "hello"; // error! x = "world"; 

In diesem Fall scheint das extrem, kann aber in anderen Situationen nützlich sein. Beispielsweise erstellen TypeScripters häufig Objekte, die in diskriminierten Gewerkschaften verwendet werden sollen.


 type Shape = | { kind: "circle", radius: number } | { kind: "square", sideLength: number } function getShapes(): readonly Shape[] { let result = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ]; // Some terrible error message because TypeScript inferred // 'kind' to have the type 'string' instead of // either '"circle"' or '"square"'. return result; } 

Die Veränderlichkeit ist eine der besten Heuristiken der Absicht, mit der TypeScript bestimmen kann, wann sie erweitert werden muss (anstatt unser gesamtes Programm zu analysieren).


Wie wir im letzten Beispiel gesehen haben, sind die Eigenschaften von JavaScript leider standardmäßig veränderbar. Dies bedeutet, dass die Sprache häufig unerwünschte Typen erweitert und an bestimmten Stellen explizite Typen erfordert.


 function getShapes(): readonly Shape[] { // This explicit annotation gives a hint // to avoid widening in the first place. let result: readonly Shape[] = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ]; return result; } 

Bis zu einem gewissen Punkt ist dies in Ordnung, aber da unsere Datenstrukturen immer komplexer werden, wird dies umständlich.


Um dies zu lösen, führt TypeScript 3.4 ein neues Konstrukt für Literalwerte ein, das als const assertions bezeichnet wird. Seine Syntax ist eine Typzusicherung mit const anstelle des const (z 123 as const B. 123 as const ). Wenn wir neue wörtliche Ausdrücke mit const Aussagen konstruieren, können wir der Sprache signalisieren, dass


  • Es sollten keine Literaltypen in diesem Ausdruck erweitert werden (z. B. kein Übergang von "hello" zu string ).
  • Objektliterale erhalten readonly Eigenschaften
  • Array-Literale werden zu readonly Tupeln

 // Type '10' let x = 10 as const; // Type 'readonly [10, 20]' let y = [10, 20] as const; // Type '{ readonly text: "hello" }' let z = { text: "hello" } as const; 

Außerhalb von .tsx Dateien kann auch die Syntax für die Assertion von spitzen Klammern verwendet werden.


 // Type '10' let x = <const>10; // Type 'readonly [10, 20]' let y = <const>[10, 20]; // Type '{ readonly text: "hello" }' let z = <const>{ text: "hello" }; 

Diese Funktion bedeutet häufig, dass Typen, die ansonsten nur verwendet würden, um dem Compiler auf Unveränderlichkeit hinzuweisen, häufig weggelassen werden können.


 // Works with no types referenced or declared. // We only needed a single const assertion. function getShapes() { let result = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ] as const; return result; } for (const shape of getShapes()) { // Narrows perfectly! if (shape.kind === "circle") { console.log("Circle radius", shape.radius); } else { console.log("Square side length", shape.sideLength); } } 

Beachten Sie, dass oben keine Typanmerkungen erforderlich sind. Mit der const Zusicherung konnte TypeScript den spezifischsten Typ des Ausdrucks verwenden.


Vorsichtsmaßnahmen


Zu beachten ist, dass const Zusicherungen nur sofort auf einfache Literalausdrücke angewendet werden können.


 // Error! // A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. let a = (Math.random() < 0.5 ? 0 : 1) as const; // Works! let b = Math.random() < 0.5 ? 0 as const : 1 as const; 

Eine andere Sache, die Sie beachten sollten, ist, dass const Kontexte einen Ausdruck nicht sofort in eine vollständig unveränderliche konvertieren.


 let arr = [1, 2, 3, 4]; let foo = { name: "foo", contents: arr, }; foo.name = "bar"; // error! foo.contents = []; // error! foo.contents.push(5); // ...works! 

Typprüfung für globalThis


Es kann überraschend schwierig sein, auf Werte im globalen Bereich zuzugreifen oder diese zu deklarieren, möglicherweise weil wir unseren Code in Modulen schreiben (deren lokale Deklarationen standardmäßig nicht auslaufen) oder weil wir möglicherweise eine lokale Variable haben, die den Namen von schattiert ein globaler Wert. In verschiedenen Umgebungen gibt es verschiedene Möglichkeiten, auf den globalen Bereich zuzugreifen - global in Knoten, window , self oder frames im Browser oder an bestimmten Orten außerhalb des strengen Modus. Nichts davon ist offensichtlich und führt häufig dazu, dass Benutzer sich nicht sicher sind, ob sie den richtigen Code schreiben.


TypeScript 3.4 globalThis Unterstützung für die Typprüfung des neuen globalThis ECMAScript - eine globale Variable, die sich auf den globalen Bereich bezieht. Im Gegensatz zu den oben genannten Lösungen bietet globalThis eine Standardmethode für den Zugriff auf den globalen Bereich, der in verschiedenen Umgebungen verwendet werden kann.


 // in a global file: let abc = 100; // Refers to 'abc' from above. globalThis.abc = 200; 

globalThis auch widerspiegeln, ob eine globale Variable als const deklariert wurde oder nicht, indem sie beim Zugriff als readonly Eigenschaft behandelt wird.


 const answer = 42; globalThis.answer = 333333; // error! 

Es ist wichtig zu beachten, dass TypeScript beim Kompilieren auf ältere Versionen von ECMAScript keine Verweise auf globalThis transformiert. Sofern Sie nicht auf immergrüne Browser abzielen (die bereits globalThis ), möchten Sie möglicherweise stattdessen eine geeignete Polyfüllung verwenden.


In benannte Parameter konvertieren


Manchmal werden Parameterlisten unhandlich.


 function updateOptions( hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number) { // .... } 

Im obigen Beispiel ist es für einen Aufrufer viel zu einfach, die Reihenfolge der angegebenen Argumente zu verwechseln. Ein gängiges JavaScript-Muster besteht darin, stattdessen ein „Optionsobjekt“ zu verwenden, sodass jede Option explizit benannt wird und die Reihenfolge keine Rolle spielt. Dies emuliert eine Funktion, die andere Sprachen als "benannte Parameter" bezeichnet haben.


 interface Options { hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number } function updateOptions(options: Options = {}) { // .... } 

Das TypeScript-Team arbeitet nicht nur an einem Compiler, sondern bietet auch die Funktionen, die Editoren für umfangreiche Funktionen wie Vervollständigungen, Definitionen und Refactorings verwenden. In TypeScript 3.4 hat unsere Praktikantin Gabriela Britto ein neues Refactoring implementiert, um vorhandene Funktionen für die Verwendung dieses Musters mit benannten Parametern zu konvertieren.


Ein Refactoring, das auf eine Funktion angewendet wird, damit benannte Parameter verwendet werden.


Obwohl wir den Namen der Funktion bis zu unserer endgültigen Version 3.4 ändern können und wir glauben, dass möglicherweise Platz für einen Teil der Ergonomie vorhanden ist, würden wir uns freuen, wenn Sie die Funktion ausprobieren und uns Ihr Feedback geben.


Änderungen brechen


Auf oberster Ebene ist this jetzt eingegeben


Der Typ der obersten Ebene wird jetzt als Typ von typeof globalThis anstelle von any typeof globalThis . Infolgedessen erhalten Sie möglicherweise Fehler beim Zugriff auf unbekannte Werte unter noImplicitAny .


 // previously okay in noImplicitAny, now an error this.whargarbl = 10; 

Beachten Sie, dass der unter noImplicitThis kompilierte Code hier keine Änderungen erfährt.


Propagierte generische Typargumente


In bestimmten Fällen kann die verbesserte Inferenz von TypeScript 3.4 zu generischen Funktionen führen, anstatt zu Funktionen, die ihre Einschränkungen übernehmen und zurückgeben (normalerweise {} ).


 declare function compose<T, U, V>(f: (arg: T) => U, g: (arg: U) => V): (arg: T) => V; function list<T>(x: T) { return [x]; } function box<T>(value: T) { return { value }; } let f = compose(list, box); let x = f(100) // In TypeScript 3.4, 'x.value' has the type // // number[] // // but it previously had the type // // {}[] // // So it's now an error to push in a string. x.value.push("hello"); 

Eine explizite Typanmerkung auf x kann den Fehler beseitigen.


Was kommt als nächstes?


TypeScript 3.4 ist unsere erste Version mit einem Iterationsplan , der unsere Pläne für diese Version umreißt und mit unserer 6-Monats-Roadmap übereinstimmen soll . Sie können beide und unsere Roadmap-Seite für fortlaufende Funktionen für alle anstehenden Arbeiten im Auge behalten.


Im Moment freuen wir uns darauf, von Ihren Erfahrungen mit dem RC zu hören. Probieren Sie es jetzt aus und teilen Sie uns Ihre Gedanken mit!

Source: https://habr.com/ru/post/de443996/


All Articles