Am 30. Juli 2018 wurde TypeScript 3.0 veröffentlicht. Die quelloffene Transpiler-Sprache aus dem Hause Microsoft feiert damit ihr knapp sechsjähriges Bestehen und da ich selbst ein großer Fan von TypeScript bin, möchte ich Euch in diesem Artikel die wichtigsten Neuerungen dieser Version kurz vorstellen.
Projektreferenzen
Unter TypeScript 3.0 können erstmalig externe TypeScript-Projekte über deren eigenständige tsconfig.json-Datei referenziert und somit als Abhängigkeit definiert werden. Über die neue Angabe references
in der tsconfig.json kann der Dateipfad externer TypeScript-Projekte angegeben und somit beim Kompilieren des Projektes berücksichtigt werden.
Die folgende Compiler-Config unter frontend/tsconfig.json referenziert die beiden Projekte backend
und e2e-test
über deren entsprechende tsconfig.json-Files:
{ "compilerOptions": { // all compiler options – as ever }, "references": [ { "path": "../backend" }, { "path": "../e2e-test" }, ], }
Beim Kompilieren eines Projektes werden die eigenen Sourcefiles neu kompiliert. Die Quelldateien der referenzierten Projekte werden nur dann kompiliert, wenn dies erforderlich ist, ansonsten werden nur deren TypeScript Declaration Files (.d.ts) verwendet. Hierdurch werden die Kompilierzeit sowei die IDE-Speichernutzung eines Projektes deutlich optimiert und zudem noch eine logische Trennung des Projektcodes gefördert.
Sinnvoll ist das beispielsweise beim Erstellen eines Microservices und der daraus resultierenden Trennung der TypeScript-Quellcodes in mehrere Projekte wie frontend
, backend
und test
.
Erweiterte Funktionen für Tupel
Tupel sind unter TypeScript bereits seit der Version 1.3 verfügbar. Mit diesem Datentyp kann ein Array definiert werden, dessen Elemente nicht aus einem einzelnen, sondern aus unterschiedlichen Datentypen bestehen können. Der folgende Codeschnipsel demonstriert den Einsatz eines Tupels:
let myFruitBowl: [ string, number ]; // compiles correctly myFruitBowl = [ 'Apple', 4 ]; // OK // compilation fails here myFruitBowl = [ 4, 'Apple' ];
Ab der Major-Version 3 bietet TypeScript nun ein paar ausgewählte neue Funktionalitäten für diesen Datentyp an:
Rest-Parameter für Tupel
Als Datentyp für Rest-Parameter war bisher lediglich ein Array zulässig. Der folgende Codeschnipsel demonstriert kurz und bündig den Einsatz eines Rest-Parameters:
function myFunction( ...args:number[] ) : void { console.log( 'Number of args: [' + args.length + ']' ); } myFunction( 0, 1, 2, 3, 4 ); // output is: 'Number of args: [5]'
Nun ermöglicht TypeScript 3.0 auch den Einsatz des Tupel-Datentyps als Rest-Parameter für Funktionen. Die folgenden beiden Funktionen sind somit ab sofort äquivalent:
function myFunctionA( ...args: [ number, string, boolean ] ) : void { } function myFunctionB( args_0: number, args_1: string, args_2: boolean ) : void { }
Spread-Expressions für Tupel
Analog zur Unterstützung des Tupel-Datentyps als Rest-Parameter einer Funktion kann dieser Typ nun auch als Spread-Expression beim Aufrufen einer Funktion angegeben werden.
Die folgenden drei Aufrufe von myFunctionA
sind daher äquivalent:
const args: [ number, string, boolean ] = [ 42, "hello", true ]; // all invocations are equivalent myFunctionA( 42, "hello", true ); myFunctionA( args[ 0 ], args[ 1 ], args[ 2 ] ); myFunctionA( ...args );
Optionale Tupel-Parameter
Ab TypeScript 3.0 können Parameter eines Tupels auch mittels der ?
-Notation als optional gekennzeichnet werden. Somit quittiert der TypeScript-Compiler das Ausbleiben eines Parameters nicht mehr als Fehler:
let myTuple: [ number, string?, boolean? ]; myTuple = [42, "hello", true]; myTuple = [42, "hello"]; myTuple = [42];
Da ich ein Verfechter von sprechendem Code und dem Clustern zusammengehöriger Felder und Informationen in sprechende Klassen und Schnittstellen bin, kann ich von der Verwendung von Tupeln nur abraten. Trotzdem begegnet einem in der Praxis und in bestimmten Anwendungsfällen dieses Konstrukt hin und wieder, weswegen es nicht schaden kann, die Einsatzmöglichkeiten und Vorkommnisse dieses Features zu kennen.
Neuer Datentyp “unknown“
Neben den wichtigsten grundlegenden Datentypen boolean
, number
, string
, undefined
, any
und null
bietet TypeScript ab Version 3.0 den neuen Datentyp unknown
an. Dieser stellt das typsichere Äquivalent zu dem nicht-typsicheren Datentyp any
dar. Einer Variable vom Typ unknown
können Werte aller beliebiger Datentypen zugewiesen werden, für den umgekehrten Vorgang bedarf es aber einer expliziten Typisierung.
Der folgende Code unter Einsatz des Datentyps any
kompiliert korrekt:
let myAny :any = 42; let myNumber :number = myAny;
Der selbe Code unter Einsatz des Datentyps unknown
wirft beim Kompilieren einen Fehler:
let myUnknown :unknown = 42; let myNumber :number = myUnknown;
Zum Fixen des Compilerproblems muss eine explizite Typisierung angegeben werden:
let myUnknown :unknown = 42; let myNumber :number = myUnknown as number;
Der Datentyp unknown
bietet sich besonders für Portierungen von (untypisiertem) JavaScript-Code nach TypeScript an und bietet dabei einen höheren Pragmatismus.
Unterstützung für “defaultProps“ in JSX-Elementen
Der Einsatz des statischen Feldes defaultProps
innerhalb von React-Komponenten wird nun auch im TypeScript-Code unterstützt.
Die folgende React-Komponente src/de/mayflower/tasklist/components/Greet.tsx kann verwendet werden, ohne dabei Properties in Form von Attributen zu übergeben. In diesem Falle werden die Properties aus dem statischen Feld defaultProps
zugewiesen:
export interface Props { name: string; } export class Greet extends React.Component<Props> { static defaultProps = { name: "world"}; render() { const { name } = this.props; return <div>Hello ${name.toUpperCase()}!</div>; } } // Type-checks! No type assertions needed! let el = <Greet />
Neue Referenzanweisung für built-in Libs
Mit der folgenden Angabe kann dem Compiler eine weitere TypeScript-Quelldatei an der angegebenen Stelle inkludiert werden. Auch kann durch diese Anweisung die explizite Reihenfolge der zu kompilierenden Dateien festgelegt werden.
/// <reference path="src/de/mayflower/util/MathUtil.ts" />
Nun kann über diese neue Referenzanweisung eine built-in Lib angegeben werden, sodass sie der Compiler beim Kompilieren berücksichtigt und die hierdurch zur Verfügung gestellten Funktionalitäten erkennt. Im folgenden Beispiel wird die String-Funktion padStart( size )
ermöglicht, die ohne die Angabe der neuen Referenzanweisung nicht gefunden würde.
/// <reference lib="es2017.string" /> console.log( 'Left padded foo: [' + ( 'foo'.padStart( 10 ) ) + ']' ); // Left padded foo: [ foo]
Diese Angabe ist äquivalent zur Verwendung der Compiler-Option
--lib es2017.string
Unter Verwendung von Webpack sind diese Referenzanweisungen allerdings obsolet, da hier mit dem Schlüsselwort import
sowie mit lib-Angaben in der webpack.config.js gearbeitet werden sollte.
TypeScript 3.0
Unterm Strich sind die Änderungen der neuen Major-Version 3.0 überschaubar. Besonders löblich ist für mich die Tatsache, dass TypeScript 3.0 mit so gut wie gar keinen Breaking Changes daherkommt und einige neue Konzepte und Ideen bereithält.
Für fachliche Rückfragen und Anregungen stehe ich Euch wie immer gerne unter christopher.stock@mayflower.de zur Verfügung.
Schreibe einen Kommentar