Mai 16, 2020

Type (some of) all the Script! 🦸🏻‍♂️

Nachdem ich früher einige Zeit mit Java und auch C# gearbeitet hatte, musste ich mich irgendwann angewidert abwenden. Die strikte Typisierung, das ganze Type Casting, der starre objektorientierte Aufbau sowie das schwerfällige Setup & Drumherum machten einfache Dinge irgendwie kompliziert.

Im Herzen blieb ich immer ein Skriptkiddie

Javascript... es war & ist so schön unkompliziert. Man macht eine Datei auf und legt quasi los. Und die dynamische Typisierung... vorhin noch ein Array, dann plötzlich ein boolean. Einfach so. Da schwebt doch Magie in der Luft. 🧙🏻‍♂️

Aber der Segen kann auch zum Fluch werden

Bei größeren Code Basen weiß irgendwann keiner mehr, was er von einer Variable erwarten kann. Es kann quasi alles drin sein und auch irgendwie nichts. Außerdem schaut JS auch nur Popcorn futternd dabei zu, wie du dich bei einer Objekteigenschaft vertippst - und damit eine ganz neue Eigenschaft anlegst. Danach beleidigt es dich dann noch mit cannot blabla of undefined.

via GIPHY

Kurzum: es bringt halt all die kleinen Problemchen mit, die dynamische Typisierung so inne hat. Ganz zu schweigen von der desaströsen IntelliSense.

Was aber, wenn wir einfach typisieren könnten, was wir für richtig halten & oft benutzen? Was, wenn wir das Beste aus zwei Welten haben könnten?

Ja was dann wohl. Das wäre natürlich mega dufte!

Und hier kommt Typescript in's Spiel. Mit Typescript können wir je nach Konfiguration plain Javascript entwickeln; wir können es aber auch so festzurren, dass wir das Gefühl bekommen, Java zu benutzen.

Ich bevorzuge den Mittelweg - und mit diesem habe ich auch direkt unseren gesamten bereits bestehenden Frontend (Mithril) Code von JS auf TS umgestellt. Sowohl die private Repos, als auch die öffentlichen npm Module.

Die Konfig dafür sieht so aus...

{
	"compilerOptions": {
		"outDir": "./dist/",
		"target": "ES5",

		"jsx": "react",
		"jsxFactory": "m",
		"module": "CommonJS",
		"moduleResolution": "Node",

		"sourceMap": false oder true,
		"declaration": true,
		"importHelpers": true,
		"removeComments": true,
		"esModuleInterop": true,
		"resolveJsonModule": true,
		"preserveConstEnums": true,
		"allowSyntheticDefaultImports": true,

		"strict": true,
		"allowJs": true,
		"checkJs": false,
		"noImplicitAny": false,
		"noUnusedLocals": true,
		"strictNullChecks": true oder false,
		"noUnusedParameters": true,
	},
	"include": [
	  "src/**/*.ts",
	  "src/**/*.tsx",
	],
	"exclude": [
		"node_modules"
	]
}

Bei npm Modulen sind sourceMaps nicht nötig und meistens sind sie so klein, dass strictNullChecks auf true gesetzt werden kann.

Bei Projekt Code ist es umgekehrt. sourceMaps sind hier sehr hilfreich, wenn wir aber strictNullChecks auf true setzen, bekommen wir wahrscheinlich 1 Trillionen build errors; das also lieber bei neuen Projekten verwenden.

Apropos strictNullChecks: Typescript bietet auch die schöne Möglichkeit des Optional Chainings (I know, bei Babel auch, aber da hat mein VSCode irgendwie rumgespackt):

// einfaches Pojo
const bla = {
    test: {
        objekt: 'Das ist ein Testobjekt.'
    }
};

// Zugriff auf "objekt", dessen Existenz nicht wirklich sicher ist, 
// ging bislang wie folgt:
const objekt = bla.test && bla.test.objekt

// Zukünftig sieht das dann einfach so aus:
const objekt = bla.test?.objekt;

// Und der Transpiler macht folgendes draus:
// (Sieht creepy aus, ich weiß, aber das interssiert uns ja letztendlich nicht.)
(_a = bla === null || bla === void 0 ? void 0 : bla.test) === null || _a === void 0 ? void 0 : _a.objekt;

Eine Sache der Übung

Am Anfang lief es noch ein wenig hakelig, mittlerweile bin ich aber recht warm damit geworden und es fängt an, Spaß zu machen. Die Unterstützung, die es bringt, ist es auf jeden Fall wert. Und wie gesagt: man muss nicht alles typisieren. Es reicht, das zu definieren, was mehrfach gebraucht wird - zentrale Objekte, Konstanten & Funktionen, etc. Das ist wahrscheinlich auch die Kunst der optimalen Verwendung.

Ab jetzt nur noch Typescript.

via GIPHY