TypeScript Essentials: Ein Praxisleitfaden für JavaScript-Entwickler
TypeScript für JavaScript-Entwickler – praktisch: Kerntypen, tsconfig, strenger Modus sowie ein schrittweiser Migrationsweg, der es ermöglicht, den Code später sicherer zu ändern.
Quellen: TypeScript for JavaScript Developers: A Practical Beginner’s Guide
Falls Sie bereits JavaScript kennen, wirkt das Erlernen von TypeScript weniger wie das Lernen einer völlig neuen Sprache und eher wie die Erweiterung einer bereits verstandenen Sprache.
TypeScript ist im Grunde JavaScript, das durch ein darübergelegtes Typsystem erweitert wurde.
Sie arbeiten weiterhin mit Variablen, Funktionen, Arrays, Objekten, Klassen, Promises, async/await, Modulen und praktisch allem anderen, was Ihnen bereits vertraut ist. Was TypeScript bietet, sind Werkzeuge, die Fehler frühzeitig erkennen, noch bevor Ihr Code in einem Browser oder auf einem Server ausgeführt wird.
Dieser Leitfaden führt Sie durch die für Entwickler, die bereits JavaScript schreiben, relevantesten TypeScript-Konzepte.
Was ist TypeScript?
TypeScript ist eine auf JavaScript aufbauende Sprache.
In den meisten Fällen ist jedes von Ihnen geschriebene JavaScript-Programm auch gültiges TypeScript. Was TypeScript beiträgt, ist die statische Typüberprüfung, und letztendlich wird es in reines JavaScript kompiliert, das in Browsern sowie unter Node.js ausgeführt wird.
Nehmen wir diesen einfachen JavaScript-Auszug als Beispiel:
function add(a, b) {
return a + b;
}
In TypeScript umgeschrieben kann dieselbe Funktion angeben, welche Werttypen sie entgegennimmt:
function add(a: number, b: number): number {
return a + b;
}
Zerlegt man das genauer:
a: numberverlangt, dassaeine Zahl ist.b: numberverlangt, dassbeine Zahl ist.- Der angehängte
: numbergibt an, dass der Rückgabewert der Funktion eine Zahl ist.
Mit diesen Anmerkungen kann TypeScript bereits vor dem Start der Ausführung eine fehlerhafte Funktionsaufrufmeldung anzeigen:
add(10, 20); // Correct
add("10", "20"); // TypeScript error
In reinem JavaScript würde der Aufruf der Funktion mit Zeichenketten-Argumenten stillschweigend "1020" zurückgeben, da der Plus-Operator die Zeichenketten verknüpfen würde anstatt Zahlen zu addieren. TypeScript erkennt dieses Art von unbeabsichtigtem Verhalten bereits im Voraus.
Warum TypeScript verwenden?
Die Flexibilität von JavaScript ist ein Vorteil, kann aber auch zu subtilen Fehlern führen.
Nehmen wir diese Funktion als Beispiel:
function calculateDiscount(price, percentage) {
return price - price * percentage / 100;
}
Der korrekte Aufruf funktioniert wie erwartet:
calculateDiscount(1000, 10);
Aber nichts hindert Sie daran, sie stattdessen so aufzurufen:
calculateDiscount("1000", "ten");
Das entstehende Problem zeigt sich erst, wenn der Code tatsächlich ausgeführt wird.
Mit TypeScript können Sie angeben, welche Typen die Funktion erwartet:
function calculateDiscount(
price: number,
percentage: number
): number {
return price - price * percentage / 100;
}
Jetzt weist TypeScript ungültige Argumente sofort aus, ohne dass der Code erst ausgeführt werden muss.
Zu den wichtigsten Vorteilen gehören:
- Fehlererkennung während des Schreibens von Code
- Bessere Unterstützung für Autocomplete in Ihrem Editor
- Einfacheres und sicheres Refactoring
- Ausdrucksstärkere Strukturen für Funktionen und Objekte
- Geeignetere Zusammenarbeit bei größeren Codebasen
- Typen, die als eingebaute Dokumentation dienen
Der Wert von TypeScript wird besonders deutlich, wenn ein Projekt über einige wenige Dateien hinauswächst oder mehrere Mitwirkende involviert ist.
Installieren von TypeScript
TypeScript kann global mit folgendem Befehl installiert werden:
npm install -g typescript
Um die installierte Version zu überprüfen, führen Sie aus:
tsc --version
Dann erstellen Sie eine Datei mit dem Namen:
app.ts
Fügen Sie etwas TypeScript-Code dazu hinzu:
const message: string = "Hello TypeScript";
console.log(message);
Kompilieren Sie die Datei mit:
tsc app.ts
Dadurch entsteht eine kompilierte JavaScript-Ausgabe:
app.js
Man kann das Ergebnis direkt mit Node.js ausführen:
node app.js
In Produktionsumgebungen übernehmen Tools und Frameworks wie React, Next.js, Angular und NestJS in der Regel automatisch diesen Kompilierungsschritt für einen.
Die Datei tsconfig.json
Die meisten TypeScript-Projekte verlassen sich auf eine tsconfig.json-Datei, um den Compiler zu konfigurieren.
Man kann eine solche Datei mit folgendem Verfahren erstellen:
tsc --init
Eine minimale Konfiguration könnte wie folgt aussehen:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"strict": true,
"outDir": "./dist",
"rootDir": "./src"
}
}
Hier sind einige der wichtigeren Einstellungen:
target
Diese legt fest, welche Version von JavaScript der Compiler ausgibt.
"target": "ES2020"
module
Diese Einstellung legt das im kompilierten Ausgabeformat verwendete Modulformat fest.
"module": "ESNext"
strict
Durch Aktivierung wird ein strengeres Niveau der Typüberprüfung eingeschaltet.
"strict": true
Für jedes neue Projekt wird dringend empfohlen, den strengen Modus eingeschaltet zu lassen.
rootDir
Dies weist dem Compiler an, wo sich Ihre TypeScript-Quelldateien befinden.
"rootDir": "./src"
outDir
Dies legt fest, wohin die kompilierte JavaScript-Ausgabe geschrieben werden soll.
"outDir": "./dist"
Grundlegende Typen
TypeScript enthält mehrere primitive Typen, die Ihnen bereits vertraut sein werden.
String
let username: string = "Vijeet";
Number
let experience: number = 10;
TypeScript macht keinen Unterschied zwischen Ganzzahlen und Fließkommazahlen; beide fallen unter number.
let age: number = 35;
let price: number = 999.99;
Boolean
let isActive: boolean = true;
Null und Undefined
let emptyValue: null = null;
let missingValue: undefined = undefined;
In Strict Mode müssen Sie null und undefined sorgfältiger handhaben.
Typableitung
Es ist nicht notwendig, jeden einzelnen Wert manuell zu annotieren.
TypeScript kann oft den Typ einer Variablen anhand dessen, was ihr zugewiesen wird, ermitteln.
let city = "Ahmedabad";
Hier erkennt TypeScript automatisch city als Zeichenkette.
Ein erneutes Zuweisen auf diese Weise löst einen Fehler aus:
city = 100;
Dieselbe Logik gilt hier ebenfalls:
let total = 500;
TypeScript schließt daraus, dass total eine Zahl enthält.
Durch die Verwendung der Typableitung bleibt Ihr Code unübersichtlicher frei. Bewahren Sie explizite Typannotierungen für Fälle auf, in denen sie tatsächlich Klarheit bringen, wie z. B. bei Funktionsparametern, API-Antwortstrukturen und komplexeren Objektstrukturen.
Arrays
Hier ist ein einfaches JavaScript-Array:
const skills = ["JavaScript", "React", "Node.js"];
TypeScript ermöglicht es Ihnen, den Elementtyp eines Arrays anzugeben:
const skills: string[] = [
"JavaScript",
"React",
"Node.js"
];
Man kann es auch mit der generischen Syntax schreiben:
const skills: Array<string> = [
"JavaScript",
"React",
"Node.js"
];
Dieselbe Logik gilt für numerische Arrays:
const scores: number[] = [75, 82, 91];
TypeScript verhindert jeden Versuch, einen Wert des falschen Typs einzufügen:
scores.push(95); // Correct
scores.push("ninety"); // Error
Objekte
Man kann einem einfachen JavaScript-Objekt eine explizite Struktur geben.
const user: {
name: string;
age: number;
isAdmin: boolean;
} = {
name: "Vijeet",
age: 35,
isAdmin: true
};
Falls eine Eigenschaft fehlt oder den falschen Typ hat, markiert TypeScript dies als Fehler.
const user: {
name: string;
age: number;
} = {
name: "Vijeet"
};
Hier fehlt der erforderliche age-Eigenschaft im Objekt.
Wenn man dieselbe Objektorstruktur an mehreren Stellen wiederverwenden muss, eignen sich Interfaces oder Typ-Alias besser als das Jedes Mal manuelle Definition der Struktur.
Interfaces
Eine Interface definiert die erwartete Struktur eines Objekts.
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
Sobald sie definiert ist, kann diese Schnittstelle überall wiederverwendet werden:
const user: User = {
id: 1,
name: "Vijeet",
email: "vijeet@example.com",
isActive: true
};
Schnittstellen sind in Situationen wie folgenden nützlich:
- die gespeicherten Details eines Benutzers zu beschreiben
- einen Katalogartikel zu modellieren
- die in einem Formular eingegebenen Felder zu validieren
- den aus einer API-Aufruf zurückgegebenen Inhalt zu strukturieren
- die in eine Komponente übergebenen Eigenschaften zu definieren
- eine Zeile aus einer Datenbanktabelle darzustellen
Zum Beispiel:
interface Product {
id: number;
title: string;
price: number;
}
Eine Funktion kann dann ein Product-Objekt als Parameter entgegennehmen:
function displayProduct(product: Product): void {
console.log(`${product.title}: ₹${product.price}`);
}
Optionale Eigenschaften
Nicht alle Objekt-eigenschaften sind immer vorhanden.
Man kennzeichnet diese, indem man nach dem Eigenschaftsnamen einen Fragezeichen setzt:
interface User {
id: number;
name: string;
phone?: string;
}
Sowohl die folgenden Objekte erfüllen die Schnittstelle:
const userOne: User = {
id: 1,
name: "Amit"
};
const userTwo: User = {
id: 2,
name: "Neha",
phone: "9876543210"
};
Da phone optional ist, wird sein Typ wie folgt:
string | undefined
Das bedeutet, Sie sollten überprüfen, ob es vorhanden ist, bevor Sie es verwenden:
if (userTwo.phone) {
console.log(userTwo.phone);
}
Typ-Aliasse
Ein Typ-Alias ermöglicht es Ihnen, einem beliebigen Typ einen wiederverwendbaren Namen zuzuweisen.
type UserId = number;
Dann können Sie diesen Alias als Typ verwenden:
const id: UserId = 101;
Typ-Aliasse kommen besonders gut zum Tragen, wenn Unionen oder komplexere Typkombinationen beschrieben werden.
type Status = "pending" | "approved" | "rejected";
Mit diesem Alias sind nur drei bestimmte Zeichenketten zulässig:
let applicationStatus: Status = "pending";
Jeder andere Versuch führt zu einem Fehler:
applicationStatus = "completed";
Interfaces gegenüber Typ-Aliasse
Sowohl Interfaces als auch Typ-Aliasse können die Struktur eines Objekts modellieren.
Hier ist die Version mit Interface:
interface Customer {
name: string;
email: string;
}
Und die entsprechende Version mit Typ-Alias:
type Customer = {
name: string;
email: string;
};
Als grobe Richtlinie für Anfänger:
- Nutzen Sie
interface, wenn Sie Objekte oder Klassenstrukturen beschreiben. - Nutzen Sie
type, wenn Sie mit Unionen, Aliassen für Primitive, Tupeln oder komplexeren kombinierten Typen arbeiten.
Dies ist jedoch keine starre Regel – in vielen Fällen funktioniert beide Ansätze genauso gut.
Union-Typen
Ein Union-Typ ermöglicht es einem Wert, einen von mehreren möglichen Typen anzunehmen.
let userId: string | number;
Sowohl Zuweisungen sind gültig:
userId = 101;
userId = "USR-101";
Union-Typen sind nützlich, wenn Daten aus der Praxis nicht immer einem festen Format folgen.
function printId(id: string | number): void {
console.log(id);
}
Bevor Sie eine Methode aufrufen, die nur auf einem der möglichen Typen vorhanden ist, müssen Sie den Wert zunächst einengen:
function printId(id: string | number): void {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(0));
}
}
Diese Technik wird als Typ-Einengung bezeichnet.
Literaltypen
Literaltypen beschränken eine Variable auf eine genaue, im Voraus definierte Menge an Werten.
type Theme = "light" | "dark" | "system";
Diese Zuweisung ist in Ordnung:
let selectedTheme: Theme = "dark";
Aber diese wird abgelehnt:
selectedTheme = "blue";
Literaltypen eignen sich gut für Fälle wie:
- den aktuellen Status eines Datensatzes
- die dem Benutzer zugewiesene Berechtigungsstufe
- eine Einstellung des visuellen Designs
- den visuellen Stil einer Schaltfläche
- das HTTP-Verb in einer Anfrage
- den Modus, in dem eine Anwendung läuft
Als Beispiel:
type UserRole = "admin" | "editor" | "customer";
Funktionen
Bei Funktionsparametern steht der Typ direkt nach dem Namen des Parameters.
function greet(name: string): string {
return `Hello, ${name}`;
}
Der Rückgabetyp der Funktion steht nach dem schließenden Klammernzeichen der Parameterliste.
function multiply(a: number, b: number): number {
return a * b;
}
Wenn eine Funktion keinen Wert zurückgibt, kennzeichnen Sie sie mit void.
function logMessage(message: string): void {
console.log(message);
}
Optionale Parameter werden mit ? markiert:
function greetUser(
name: string,
title?: string
): string {
if (title) {
return `Hello, ${title} ${name}`;
}
return `Hello, ${name}`;
}
Standardwerte für Parameter verhalten sich genauso wie in herkömmlichem JavaScript:
function calculateTax(
amount: number,
rate: number = 18
): number {
return amount * rate / 100;
}
Typisierung von Arrow-Funktionen
Auch Arrow-Funktionen können auf dieselbe Weise annotiert werden.
const subtract = (
a: number,
b: number
): number => {
return a - b;
};
Hier ist eine kompaktere Form:
const subtract = (
a: number,
b: number
): number => a - b;
Der any-Typ
Durch Zuweisen des any-Types an einen Wert weist man TypeScript an, die Überprüfung gänzlich einzustellen.
let data: any = "Hello";
data = 100;
data = true;
data = { name: "Vijeet" };
Auch wenn dies auf den ersten Blick praktisch erscheint, führt zu häufiger Verwendung von any dazu, dass der größte Teil dessen, was TypeScript bietet, ungenutzt bleibt.
Nehmen Sie dieses Beispiel:
function processData(data: any): any {
return data.someProperty.someMethod();
}
TypeScript kann nichts tun, um Fehler in dieser Funktion aufzudecken.
Reservieren Sie any für Situationen, in denen es tatsächlich unvermeidlich ist – beispielsweise bei schrittweiser Umwandlung einer JavaScript-Codebasis oder beim Umgang mit Drittanbieterdaten, die keine korrekten Typdefinitionen aufweisen.
Der unknown-Typ
unknown bietet eine sichere Möglichkeit, Werte darzustellen, deren Typ im Voraus nicht bekannt ist.
let value: unknown;
Genauso wie any akzeptiert es jede Art von Wert:
value = "Hello";
value = 100;
value = true;
Aber im Gegensatz zu any zwingt TypeScript Sie dazu, den Typ vor der tatsächlichen Verwendung einzugrenzen:
if (typeof value === "string") {
console.log(value.toUpperCase());
}
Deshalb eignet sich unknown hervorragend für:
- Daten aus externen APIs
- Werte, die aus JSON解析stammen
- Fehlerobjekte, die in einem try/catch-Block gefangen werden
- Eingaben von Benutzern
- Alles, dessen Struktur noch nicht festgelegt ist
Typbehauptungen
Es gibt Situationen, in denen man über einen Wert mehr Informationen hat, als TypeScript allein ableiten kann.
Eine Typbehauptung ermöglicht es dem Compiler, zu bestimmen, wie mit diesem Wert umgegangen werden soll.
const input = document.getElementById(
"email"
) as HTMLInputElement;
Sobald eine Behauptung vorgenommen wurde, kann man auf Eigenschaften zugreifen, die spezifisch für diesen Typ sind:
console.log(input.value);
Denken Sie daran, dass eine Behauptung zur Laufzeit nichts ändert – sie verändert lediglich, wie TypeScript den Wert während der Kompilierung interpretiert.
Deshalb sollten Sie Behauptungen sparsam verwenden und nur dann, wenn Sie sich sicher sind, wie die Daten tatsächlich strukturiert sind.
Tupel
Ein Tupel ist ein Array mit einer festgelegten Anzahl von Positionen, wobei jede Position ihren eigenen definierten Typ hat.
let userEntry: [number, string];
userEntry = [1, "Vijeet"];
Hier muss die erste Position eine Zahl und die zweite eine Zeichenkette enthalten.
Was folgt, würde fehlschlagen:
userEntry = ["Vijeet", 1];
Tupel eignen sich gut für kleine, fest strukturierte Daten oder paarweise angeordnete Werte:
type Coordinate = [number, number];
const location: Coordinate = [23.0225, 72.5714];
Wenn die Daten komplexer werden oder beschreibende Feldnamen benötigt werden, sind einfache Objekte in der Regel klarer.
Enums
Mit Enums können Sie eine feste Gruppe benannter Konstanten definieren.
enum OrderStatus {
Pending,
Processing,
Shipped,
Delivered
}
Man kann darauf wie folgt verweisen:
let status: OrderStatus = OrderStatus.Processing;
Das Zuweisen von Zeichenfolgenwerten an Enums verbessert oft die Lesbarkeit:
enum PaymentStatus {
Pending = "pending",
Paid = "paid",
Failed = "failed"
}
Trotzdem bevorzugen viele TypeScript-Nutzer stattdessen Union-Typen, insbesondere bei einfacheren Szenarien:
type PaymentStatus =
| "pending"
| "paid"
| "failed";
Union-Typen ergeben in der Regel weniger JavaScript-Code und sind eine solide Standardlösung für den größten Teil des Frontend-Codes.
Klassen
TypeScript erweitert gewöhnliche JavaScript-Klassen um Typüberprüfungen.
class Employee {
name: string;
salary: number;
constructor(name: string, salary: number) {
this.name = name;
this.salary = salary;
} getAnnualSalary(): number {
return this.salary * 12;
}
}
Die Instanziierung der Klasse sieht genauso aus wie in JavaScript:
const employee = new Employee("Rahul", 50000);
console.log(employee.getAnnualSalary());
Access-Modifikatoren sind ebenfalls Teil der Klasse-Syntax von TypeScript.
class BankAccount {
public accountHolder: string;
private balance: number;
constructor(accountHolder: string, balance: number) {
this.accountHolder = accountHolder;
this.balance = balance;
} public getBalance(): number {
return this.balance;
}
}
Die wichtigsten Modifikatoren sind:
public: sichtbar von überallprivate: nur innerhalb der Klasse selbst sichtbarprotected: sichtbar innerhalb der Klasse und aller Unterklassenreadonly: kann einmal gesetzt werden, aber danach nicht mehr überschrieben
Generics
Generics ermöglichen es Ihnen, flexible, wiederverwendbare Code zu schreiben, der dennoch eine vollständige Typsicherheit bei verschiedenen Eingabetypen bewahrt.
Nehmen Sie diese Funktion als Ausgangspunkt:
function returnValue(value: any): any {
return value;
}
Da sie auf any angewiesen ist, kann TypeScript Ihnen nichts über das Ergebnis sagen.
Durch Umschreiben mit einem generischen Parameter wird das behoben:
function returnValue<T>(value: T): T {
return value;
}
Nun stimmt die Rückgabetypen mit dem übergabenen Wert überein:
const text = returnValue<string>("Hello");
const count = returnValue<number>(100);
In vielen Fällen kann TypeScript den generischen Typ automatisch ableiten, sodass man ihn nicht explizit angeben muss:
const text = returnValue("Hello");
const count = returnValue(100);
Generics kommen ständig zum Einsatz bei der Arbeit mit Arrays, API-Antwortstrukturen, wiederverwendbaren UI-Komponenten, benutzerdefinierten Hooks und Hilfsfunktionen.
function getFirstItem<T>(items: T[]): T | undefined {
return items[0];
}
So wird es verwendet:
const firstNumber = getFirstItem([10, 20, 30]);
const firstName = getFirstItem(["Amit", "Neha"]);
Promises und Asynchrone Funktionen
Funktionen, die mit async deklariert sind, ergeben immer Promises.
async function getUsername(): Promise<string> {
return "Vijeet";
}
Eine asynchrone Funktion, die ein Benutzerobjekt zurückgibt, könnte so aussehen:
interface User {
id: number;
name: string;
}
async function getUser(): Promise<User> {
return {
id: 1,
name: "Vijeet"
};
}
Und für eine Liste von Benutzern würde man den Rückgabetyp in ein Array packen:
async function getUsers(): Promise<User[]> {
return [
{ id: 1, name: "Vijeet" },
{ id: 2, name: "Rahul" }
];
}
Durch das Zuweisen richtiger Typen an die API-Ergebnisse wird der Rest Ihres Codebases vor Fehlern in späteren Schritten geschützt.
Typisierung von API-Antworten
Stellen Sie sich einen Endpunkt vor, der Produktdaten zurückgibt:
interface Product {
id: number;
title: string;
price: number;
}
Man kann den fetch-Aufruf in eine Funktion einbetten, die ihren Rückgabetyp deklariert:
async function fetchProducts(): Promise<Product[]> {
const response = await fetch("/api/products");
if (!response.ok) {
throw new Error("Unable to fetch products");
} const data: Product[] = await response.json(); return data;
}
Es gibt einen wichtigen Punkt, den man im Gedächtnis behalten sollte: TypeScript kann diese Daten nicht überprüfen, sobald die Anwendung tatsächlich läuft.
Die Zeile:
const data: Product[] = await response.json();
weist den Compiler lediglich an, die parsierte Antwort als Product[] zu behandeln – sie bestätigt nicht, dass der Server tatsächlich Objekte in dieser Form zurückgesendet hat.
In Anwendungen, bei denen die Korrektheit von großer Bedeutung ist, kombinieren Entwickler häufig TypeScript mit einer Laufzeit-Validierungsbibliothek wie Zod, um diese Lücke zu schließen.
Bearbeitung von DOM-Elementen
Browser-Elementtypen sind in TypeScript bereits enthalten.
const button = document.querySelector(
"#submit-button"
);
Der Compiler weiß, dass diese Abfrage leer zurückkommen könnte, daher umfasst der Ergebnistyp neben dem Elementtyp auch null.
Das bedeutet, Sie sollten vor der Verwendung auf den fehlenden Fall achten:
if (button) {
button.addEventListener("click", () => {
console.log("Button clicked");
});
}
Falls Sie einen spezifischeren Elementtyp benötigen, können Sie dies über eine Assertion sicherstellen:
const emailInput = document.querySelector(
"#email"
) as HTMLInputElement | null;
Dann lesen Sie seinen Wert, nachdem Sie bestätigt haben, dass er existiert:
if (emailInput) {
console.log(emailInput.value);
}
TypeScript ersetzt keine Laufzeitvalidierung
Dieser Punkt verdient besondere Betonung, insbesondere für Entwickler, die direkt aus JavaScript kommen.
Die Überprüfungen in TypeScript finden nur während der Entwicklung und beim Kompilieren statt. Sobald Ihr Code in JavaScript kompiliert wird, verschwinden alle Typangaben.
Nehmen Sie diese Funktion:
function double(value: number): number {
return value * 2;
}
Nach der Kompilierung sieht sie ungefähr so aus:
function double(value) {
return value * 2;
}
Zu diesem Zeitpunkt bleibt keine Spur der ursprünglichen Typinformationen mehr übrig, um etwas durchzusetzen.
Deshalb kann TypeScript nicht garantieren, dass:
- eine API tatsächlich gut strukturierte Daten zurückgibt
- ein Benutzer einen gültigen Wert eingegeben hat
- ein Datenbankeintrag der erwarteten Struktur entspricht
- eine Drittanbieterdienstleistung ihr eigenes dokumentiertes Vertragswerk einhält
Alles, was von außerhalb Ihres Codes kommt, muss während des Programmablaufs weiterhin überprüft werden.
Migration eines bestehenden JavaScript-Projekts
Die Umwandlung einer JavaScript-Codebasis in TypeScript muss nicht in einem einzigen umfassenden Umbau erfolgen.
Eine schrittweise Migration funktioniert in der Praxis in der Regel besser.
Schritt 1: TypeScript installieren
npm install --save-dev typescript
Schritt 2: tsconfig.json erstellen
npx tsc --init
Schritt 3: Vorübergehend JavaScript zulassen
{
"compilerOptions": {
"allowJs": true,
"checkJs": false
}
}
Schritt 4: Dateien schrittweise umbenennen
Ändern Sie eine Datei wie folgt:
utils.js
in:
utils.ts
Und bei React-Komponenten ändern Sie:
Button.jsx
in:
Button.tsx
Schritt 5: Fehler nacheinander in einer Datei beheben
Es hilft, mit Hilfsfunktionen, gemeinsamen Datenschemata und kleineren Komponenten zu beginnen und die komplexere Anwendungslogik auf später zu verschieben.
Der schrittweise Ansatz ist eines der größten praktischen Vorteile von TypeScript, wenn es in ein bestehendes Projekt integriert wird.
Häufige Fehler von JavaScript-Entwicklern
any überall verwenden
const user: any = getUser();
Dies mag eine sofortige Compilerwarnung unterdrücken, doch dadurch wird auch die gesamte Typüberprüfung beseitigt.
Ein besseres Vorgehen ist es, eine echte Schnittstelle zu definieren:
interface User {
id: number;
name: string;
}
Typen zu jeder Variablen hinzufügen
Es ist nicht notwendig, dies zu schreiben:
const name: string = "Vijeet";
const count: number = 10;
const active: boolean = true;
denn die Typinferenz ermittelt diese Typen bereits von selbst:
const name = "Vijeet";
const count = 10;
const active = true;
Reservieren Sie explizite Anmerkungen für Fälle, in denen sie tatsächlich Klarheit schaffen.
Ablehnung von null
Dieser Code kann zur Laufzeit fehlschlagen:
const element = document.querySelector("#app");
element.innerHTML = "Hello";
Das abgerufene Element existiert möglicherweise einfach nicht auf der Seite.
Schützen Sie sich lieber vor dieser Möglichkeit:
const element = document.querySelector("#app");
if (element) {
element.innerHTML = "Hello";
}
Verwendung von Typbehauptungen zur Verdeckung von Problemen
Es ist riskant, etwas Derartiges zu schreiben:
const user = apiResponse as User;
Die Behauptung überprüft nichts bezüglich der tatsächlichen API-Antwort – sie sagt dem Compiler lediglich, seinem Wort Glauben zu schenken.
TypeScript als unterschiedliche Laufzeitumgebung betrachten
TypeScript wird nicht direkt vom Browser ausgeführt. Es wird vor dem Ausführen in reines JavaScript kompiliert.
Das bedeutet, alles, was Sie bereits über JavaScript wissen, gilt weiterhin.
Ein praktisches Beispiel
Betrachten Sie einen einfachen Warenkorb, der auf die übliche Weise in JavaScript erstellt wird, nun jedoch mit TypeScript-Typen ergänzt ist.
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
function calculateCartTotal(
items: CartItem[]
): number {
return items.reduce((total, item) => {
return total + item.price * item.quantity;
}, 0);
}
const cart: CartItem[] = [
{
id: 1,
name: "Keyboard",
price: 2500,
quantity: 1
},
{
id: 2,
name: "Mouse",
price: 1200,
quantity: 2
}
];
const total = calculateCartTotal(cart);
console.log(`Cart total: ₹${total}`);
Durch diese Maßnahme ist die Funktion nun vor fehlerhaften Eingaben im Warenkorb geschützt.
Zum Beispiel wird der folgende Codeausschnitt nicht kompilieren:
const invalidCart: CartItem[] = [
{
id: 1,
name: "Keyboard",
price: "2500",
quantity: 1
}
];
Das Problem ist, dass price als Zeichenkette gesetzt wurde, obwohl es eigentlich eine Zahl sein sollte.
Soll jedes JavaScript-Projekt TypeScript verwenden?
Nicht immer.
Reines JavaScript funktioniert weiterhin gut für Fälle wie:
- Kleine Experimente
- Einfache Skripte
- Schnelle Prototypen
- Basische Landing Pages
TypeScript bringt in solchen Situationen in der Regel mehr Vorteile mit sich:
- Große Frontend-Anwendungen
- Codebasen für React und Next.js
- Node.js-Backend-Dienste
- Codebasen, die zwischen Teams geteilt werden
- Langlebige Produkte
- Projekte, die mit komplexen Datenstrukturen arbeiten
- Anwendungen, die von mehreren Entwicklern erstellt und gewartet werden
Im Allgemeinen gilt: Je größer und langlebiger ein Projekt ist, desto mehr Vorteile ergeben sich aus der Verwendung von TypeScript.
Fazit
TypeScript ersetzt JavaScript nicht – es verbessert vielmehr das Schreiben von JavaScript, indem es den Code mit einer zusätzlichen Sicherheitsschicht umhüllt.
Als jemand, der bereits mit JavaScript arbeitet, verfügen Sie bereits über den größten Teil des notwendigen Hintergrundwissens:
- Variablen
- Funktionen
- Objekte
- Arrays
- Klassen
- Promises
- Module
- Asynchrone Code
Was noch gelernt werden muss, ist hauptsächlich, wie man die Struktur der Daten darstellt.
Eine gute Ausgangslage umfasst:
- Primitivtypen
- Parameter- und Rückgabetypen für Funktionen
- Interfaces
- Union-Typen
- Optionale Eigenschaften
- Generics
- Typisierte Antworten von APIs
Versuchen Sie nicht, sofort alle fortgeschrittenen Funktionen zu erlernen. Beginnen Sie damit, gewöhnlichen JavaScript-Code zu annotieren, und lassen Sie die Anforderungen Ihrer tatsächlichen Projekte bestimmen, was Sie als Nächstes lernen.
Sobald TypeScript zur zweiten Natur wird, kann der Rückgriff auf reines, untypisiertes JavaScript unerwartet unsicher erscheinen.