
Microsoft hat TypeScript veröffentlicht, laut offizieller Ansage eine Programmiersprache for application-scale JavaScript development
. Meiner Wahrnehmung nach wurde TypeScript in der Webentwickler-Welt mit einer gewissen Skepsis aufgenommen, was ich spontan durchaus nachvollziehbar finde – immerhin ist es eine Technologie für das Web von Microsoft und das kollektive Gedächtnis der Webentwickler vergisst kleine Sünden wie den IE6 und andere Untaten nicht so schnell.
Aber da ich es in meinen HTML5-Seminaren immer öfter mit Entwicklern aus der Microsoft-Ecke zu tun habe, komme ich nicht umhin, mir TypeScript mal vorurteilsfrei reinzuziehen, denn irgendwer wird bestimmt mal danach fragen. Das Reinziehen ist gar nicht mal so trivial, denn leider versteckt die die offizielle Webseite viele nützliche Informationen zugunsten von eher Marketing-fokussierter Prosa (auf deren Inhalt sich dann auch die meisten der hämischen Kommentare beziehen). Ich habe es aber trotzdem mal anhand der Specs versucht. In diesem ersten Teil will ich versuchen, die wichtigsten Fakten rund um TypeScript herauszuarbeiten, mein Kommentar zu dem Ganzen folgt dann in einem zweiten Artikel.
Was ist TypeScript?
TypeScript ist ein Superset von JavaScript – jedes gültige JavaScript-Programm ist auch ein (syntaktisch) gültiges TypeScript-Programm. Die Erweiterungen von TS werden vom Compiler in stinknormales JavaScript umgewandelt, so dass man TypeScript mit jedem Browser der Welt oder auch mit Node.js benutzen kann, solange man es vorher kompiliert. Der Vergleich mit Googles ungleich ambitionierterem Dart ist also weniger zutreffend als z.B. mit CoffeeScript. Bei Dart ist die JavaScript-Kompilierung nur eine Krücke, bei TypeScript das Endziel.
Die Features, die TypeScript neu einführt, fallen in zwei Kategorien: einerseits gibt es ein optionales Typsystem, andererseits sind mehrere Funktionen aus ECMAScript 6 übernommen worden. Das Typsystem erlaubt es, Variablen fest einen Typ zuzuweisen. Diese Information können Compiler und IDEs benutzen, um Fehler zu schmeißen bzw. bessere Autovervollständigung anzubieten. Die ES6-Features ermöglichen es, so zu programmieren, als sei der neue JavaScript-Standard (bzw. Teile davon) bereits fertig und in allen Browsern zu finden. Das Endprodukt ist trotz all dieser Features immer komplett normales JS, so wie man es auch händisch schreiben würde. Selbst wenn man volles Rohr auf TS-Features setzt …
class Auto {
private kilometer: number = 0;
constructor(public modell, public baujahr){}
public fahren(km: number){
this.kilometer += km;
}
public getKilometer(){
return this.kilometer;
}
}
interface Fahrzeug {
modell: string;
baujahr: number;
getKilometer: () => {};
}
var karre = new Auto('VW Käfer', 1984);
karre.fahren(20);
function describe(auto: Fahrzeug) {
return "Ein " + auto.baujahr + "er " + auto.modell +
" mit " + auto.getKilometer() + "km auf der Uhr";
}
… ist das Endprodukt so normales JavaScript, dass es selbst im IE6 laufen würde:
var Auto = (function () {
function Auto(modell, baujahr) {
this.modell = modell;
this.baujahr = baujahr;
this.kilometer = 0;
}
Auto.prototype.fahren = function (km) {
this.kilometer += km;
};
Auto.prototype.getKilometer = function () {
return this.kilometer;
};
return Auto;
})();
var karre = new Auto('VW Käfer', 1984);
karre.fahren(20);
function describe(auto) {
return "Ein " + auto.baujahr + "er " + auto.modell + " mit " + auto.getKilometer() + "km auf der Uhr";
}
Was also ist Typescript? Es ist kein Dart, sondern eigentlich nur ein JavaScript-Dialekt, dessen Compiler ein Typsystem und einen ES6-Transpiler implementiert und dessen Endprodukt stinknormales JavaScript ist.
How to Typescript
Der TypeScript-Compiler ist (natürlich) in TypeScript geschrieben und fluppt daher in jeder handelsüblichen JavaScript-Umgebung. Als Open-Source-Software (Apache License) lässt sich das gute Stück entweder über CodePlex (eine Art Github für MS) oder via npm install -g typescript
in Node.js beschaffen. Die Datei foo.ts
kompiliert man dann durch den Aufruf tsc foo.ts
und erhält dafür die Datei foo.js
.
Als Tools gibt es neben dem Plugin für Visual Studio 2012 auch Erweiterungen für Vim, Emacs und Sublime Text 2. Darüber hinaus existiert auch eine Online-Sandbox.
Das Typsystem
JavaScript ist eine Sprache mit einem schwachen, dynamischen Typsystem. Das lässt sich komödiantisch ausschlachten (ab 01:20), hat aber primär den Effekt, dass ein JS-Programmierer dem Interpreter weder vorbeten muss, welche Datentypen gewisse Objekte haben noch sich manuell um deren ggf. nötige Umwandlung kümmern muss. Die JS-Engine erkennt die Art der verwendeten Datentypen selbstständig (Duck Typing) und wenn zwei Objekte unterschiedlichen Typs aufeinander treffen, wird einer der beiden Unfallgegner automatisch konvertiert:
// Das sieht wie ein String aus
var s = "Hallo Welt";
// Das sieht wie eine Zahl aus
var n = 1337;
// String plus Zahl geht nicht, also wird die Zahl
// gemäß der JS-Regeln vor der Operation umgewandelt
var x = s + n; // Ergibt den String "Hallo Welt1337"
Dieses Feature lässt allerdings nicht wenigen Programmieren die Haare zu Berge stehen. Einerseits befürchten sie, dass bei versehentlichen Typumwandlungen schwer zu diagnostizierende Bugs entstehen und sie hätten es lieber, wenn bei der Kollision zweier Typen ein Fehler gemeldet wird. Andererseits soll ein dynamisches Typsystem auch die Entwicklung von IDEs mit intelligenter Autovervollständigung und anderen fortschrittlichen Funktionen schwerer machen. Als Antwort auf diese Bedenken führt TypeScript ein statisches Typsystem ein, das alle Wünsche der Haare-zu-Berge-Fraktion erfüllt.
In TypeScript-Code kann bei Variablen angegeben werden, welchen Typ sie haben sollen:
// Ein String
var s: string = "Hallo Welt";
// Eine Zahl
var n: number = 1337;
Diese Typannotationen sind optional. Fehlen sie, findet der TS-Compiler selbst heraus, welchen Datentyp ein Objekt hat (Typinferenz). Sinn des Ganzen ist, dass Tools (und Entwickler) sofort erkennen können, welche Sorte von Objekt in einer Variable vorkommen sollte und der Compiler kann Alarm schlagen, wenn diese Regeln mißachtet werden:
// Der Compiler meldet: "Cannot convert 'number' to 'string'"
var s: string;
s = 42;
// Der Compiler meldet: "Supplied parameters do not match any signature of call target"
function sagHallo(name: string){
return "Hallo " + name;
}
sagHallo(42);
Das Ganze funktioniert einerseits mit den aus JS bekannten Primitiven (String, Number usw.), kann aber auch auf Objekte angewendet werden. So lässt sich zum Beispiel ein Array definieren, das nur Strings enthalten darf:
var arr: string[] = [];
Für Eigenbau-Objekte lässt sich ein Interface definieren, dass festlegt, wie ein Objekt aufgebaut sein muss:
interface Fahrzeug {
modell: string;
baujahr: number;
}
function describe(auto: Fahrzeug) {
return "Ein " + auto.baujahr + "er " + auto.modell;
}
describe({
modell: 'Käfer',
baujahr: 1970
});
Auch Funktionen können als Typ angegeben werden … mitsamt der Argumente, die die Funktion übergeben bekommen soll. Das erscheint besonders bei Callbacks sinnvoll:
function fireCb(callback: (i: number) => {}){
for(var i = 0; i < 5; i++){
callback(i);
}
}
// Klappt
fireCb(function(i){
console.log(i);
});
// Klappt nicht, da das Argument "foo" nicht gewünscht ist
// Supplied parameters do not match any signature of call target:
// Call signatures of types '(i: any,foo: any) => any' and '(i: number) => {}' are incompatible:
// Call signature expects 1 or fewer parameters
fireCb(function(i, foo){
console.log(i);
});
In diesem Code fällt der etwas befremdlich aussehende Schnipsel (i: number) => {}
ins Auge. Dabei handelt es sich um eine neue Schreibweise für Funktionen, die in ECMAScript 6 eingeführt werden soll und die in Typescript bereits unterstützt wird.
Die ES6-Features
ECMAScript 6 ist die kommende Ausbaustufe von JavaScript (zu der ich rein zufällig eine Artikelserie parat habe) und befindet sich in einem Zustand höchster Unfertigkeit. Ungeachtet dessen gibt es bereits einige Programme, die ES6-Code in heute schon funktionierendes JavaScript übersetzen – die sogenannten Transpiler. Auch in TypeScript steckt ein solcher Transpiler und manches ES6-Feature kann hier schon benutzt werden.
Die etwas komisch aussehende Funktionssystax entspringt (das ist kein Witz) primär dem Wunsch, nicht mehr das lange Wort function
ausschreiben zu müssen. Und wenn man schon dabei ist, könnte man die neue Form doch auch gleich mit einem besonderen Scope-Binding ausstatten. So ging man also hin, borgte sich von CoffeeScript den fat arrow und schon war man fertig:
var fn = () => {
console.log(this.foo);
};
// Entspricht (und kompiliert in TypeScript zu):
var _this = this;
var fn = function () {
console.log(_this.foo);
};
Ebenfalls in ES6 geplant und in Typescript umgesetzt ist ein Klassensystem, das zwar recht simpel ist, aber grundsätzlich alles an Bord hat, was man so erwarten würde:
class Auto {
private kilometer: number = 0;
constructor(public modell: string, public baujahr: number){}
public fahren(km: number){
this.kilometer += km;
}
public getKilometer(){
return this.kilometer;
}
}
var karre = new Auto('VW Käfer', 1984);
karre.fahren(20);
var tacho = karre.getKilometer(); // 20
Der Code, den der Typescript-Compiler hieraus erzeugt, ist eine relativ simple Constructorfunktion nebst Prototype, bei dem die private
-Properties nicht besonders (z.b. durch Closure-Versteckspiele) geschützt sind:
var Auto = (function () {
function Auto(modell, baujahr) {
this.modell = modell;
this.baujahr = baujahr;
this.kilometer = 0;
}
Auto.prototype.fahren = function (km) {
this.kilometer += km;
};
Auto.prototype.getKilometer = function () {
return this.kilometer;
};
return Auto;
})();
Allein der Compiler verhindert, dass ein direkter Zugriff auf private Objekte wie karre.kilometer
erfolgt.
Wer Klassen haben will, möchte diese vermutlich auch in Modulen organisieren natürlich haben ES6 und TypeScript entsprechende Funktionalität an Bord:
// modul.ts
export class Auto {
private kilometer: number = 0;
constructor(public modell: string, public baujahr: number){}
public fahren(km: number){
this.kilometer += km;
}
public getKilometer(){
return this.kilometer;
}
}
// main.ts
import fahrzeug = module("modul");
var karre = new fahrzeug.Auto('VW Auto', 1984);
karre.fahren(20);
Der Compiler baut aus modul.ts
wahlweise CommonJS-Module für den Einsatz in Node.js und anderen serverseitigen Umgebungen …
var Auto = (function () {
function Auto(modell, baujahr) {
this.modell = modell;
this.baujahr = baujahr;
this.kilometer = 0;
}
Auto.prototype.fahren = function (km) {
this.kilometer += km;
};
Auto.prototype.getKilometer = function () {
return this.kilometer;
};
return Auto;
})();
exports.Auto = Auto;
… oder AMD-Module für RequireJS und Konsorten:
define(["require", "exports"], function(require, exports) {
var Auto = (function () {
function Auto(modell, baujahr) {
this.modell = modell;
this.baujahr = baujahr;
this.kilometer = 0;
}
Auto.prototype.fahren = function (km) {
this.kilometer += km;
};
Auto.prototype.getKilometer = function () {
return this.kilometer;
};
return Auto;
})();
exports.Auto = Auto;
});
Wenn eines Tages ES6-Module wie geplant Einzug in die JavaScript-Engines dieser Welt finden, wird diese Übersetzung dann vermutlich überflüssig.
Fazit
Zusammengefasst ist TypeScript also ein JavaScript-Superset, das JS um Features aus der ECMAScript-Zukunft sowie ein eigenes Typsystem erweitert. Da all diese Features ausschließlich im Compiler stattfinden und das Endprodukt normales JavaScript ist, sind keine Browser-Updates oder Polyfill-Scripts nötig um mit TS loszulegen.
Die Preisfrage ist nun: braucht man das? Wenn ja, wer braucht das? Will man das überhaupt in der JavaScript-Welt herumgeistern sehen? Und wird TypeScript eine Nutzerschaft finden oder in der Bedeutungslosigkeit versinken? Meine Meinung dazu gibt es die Tage in Teil 2.
Kommentare (8)
Chris ¶
8. Oktober 2012, 14:32 Uhr
Irgendwie sehe ich dem ganzen zwiespältig gegenüber. Einerseits ist die Typisierung schon etwas, was Fehler verhindern kann (aber nicht muss!), die Klassen-, Interface-, ..-schreibweise ist wesentlich angenehmer und einfach zu lesen wie in JavaScript, aber was ich noch immer nicht verstehe ist, dass z.B. wie oben im genannten Beispiel mit der "private" Variable, dies nur dann im TS als Fehler bemängelt wird, aber dennoch, in meinen Augen, falsch kompiliert wird.
Am Ende baut man nur ums Problem rum, aber das Problem bleibt dennoch bestehen.
Ich schätze mal, dass es ähnlich wie bei vielen MS-Projekten nur ein "dabeisein ist alles" ist...aber Innovationen sehen anders aus (z.B. gängige Standards in Browser integrieren / mitentwickeln und kein eigenes 10 Jahre-Support-Süppchen kochen).
Peter Kröner ¶
8. Oktober 2012, 14:41 Uhr
Findest du nicht, dass MS heutzutage fleißig dabei ist, Standards zu integrieren und innovativ mitzuentwickeln? Mir fallen einige (Standard)-Technologien ein, bei denen IE10 ganz vorne mit dabei ist, z.B. Grid Layout.
Chris ¶
8. Oktober 2012, 15:26 Uhr
Da stimme ich dir natürlich zu.
Aber man muss dann auch als Gegenargument erwähnen, dass das nur mit dem neuesten Browser, welcher die neueste Software aus dem eigenen Hause voraussetzt, funktioniert. Dennoch weigern Sie sich, den alten "Kram" zu entfernen. Die Medaille hat leider zwei Seiten :-(
Fabian ¶
8. Oktober 2012, 15:38 Uhr
Du solltest mal BarCamps besuchen und die dort inzwischen oft stattfindenden "Bash Microsoft"-Sessions besuchen. Die wohl am häufigsten gestellte Frage ist da nämlich: "Wieso supportet ihr noch IE6/7/8?". Die ganz einfache und für jeden, der die Welt ein bisschen versteht, verständliche Antwort: Wir müssen. Wir sind das größte Softwareunternehmen der Welt und haben Kunden, die uns viel Geld dafür bezahlen, Software auch in 15 Jahren noch nutzen zu können.
Christian H. ¶
8. Oktober 2012, 16:45 Uhr
Microsoft gibt sich teilweise sehr große Mühe, den alten Kram zu entfernen, kann aber nicht den Support beenden, da noch Bindung aus alten Support-Verträgen vorhanden ist.
Auch war ich über IE9 und insbesondere IE10 positiv überrascht, da fliegen sogar die Conditional Commments raus
Auch sehe ich nicht, warum sich die Welt für eine Sprache (Dart, TypeScript, JavaScript, oder etwas anderes) entscheiden sollen muss.
Diese Technologien könnten durchaus parallel mit einander bestehen.
Wann gibt es eigentlich etwas neues aus ECMAScript 6?
Peter Kröner ¶
8. Oktober 2012, 16:59 Uhr
Das ist positiv?
Von mir jetzt? Also vier Entwürfe liegen schon im CMS, mindestens einer davon hat auch schon den Status „so gut wie fertig“. Ich komme nur leider im Moment zu kaum was :(
BenW ¶
9. Oktober 2012, 14:55 Uhr
Freue mich schon auf den zweiten Teil zu diesem Artikel! Ich finde die Ambitionen recht gut. Wenn niemand CoffeeScript entwickelt hätte wären vielleicht nicht sinnvolle Elemente daraus in ECMAScript 6 eingeflossen. Und da es ja auch keine wirkliche Konkurrenz zu JavaScript in dem Sinne ist, sehe ich auch keine wirklichen Argumente dagegen. Es kann sich jeder frei fühlen es zu nutzen oder nicht.
Remo Zumsteg ¶
17. April 2013, 23:39 Uhr
Der Benutzungsbereich von Javascript hat sich meiner Meinung nach in den letzten Jahren stark vergrössert. Wenn man früher JS noch für reine Frontend-Entwicklung verwendet hat, tummelt sich heutzutage immer mehr JS in der eigentlichen Businesslogik und im Model (siehe Mobile-Apps, Windows 8 Apps, WebSql etc). Somit ist es für mich nicht verwunderlich, dass erstens immer mehr "Backendprogrammierer" nach HTML5/JS abrutschen und zweitens stärker nach statischer Typisierung, etc. geschrien wird.
Mir persönlich geht die Entwicklung von ECMA (und somit auch TypeScript) in die richtige Richtung.