blog / warum-ich-typescript-liebe

Warum ich TypeScript liebe (und du es auch solltest)

Von Skeptiker zum TypeScript-Evangelisten

Vor 3 Jahren dachte ich: “TypeScript? Das ist nur JavaScript mit extra Steps.”

Heute denke ich: “Wie konnte ich jemals ohne TypeScript arbeiten?”

Hier ist meine Geschichte und warum TypeScript mein Leben als Developer verändert hat.

1. Bugs zur Compile-Time statt zur Runtime

Früher (JavaScript):

function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// 3 Monate später in Production:
calculateTotal(null); // Cannot read property 'reduce' of null

Bug wird erst entdeckt: Wenn ein User darauf klickt und ein Error Report reinkommt.

Heute (TypeScript):

interface Item {
  price: number;
  name: string;
}

function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// Der Editor zeigt SOFORT:
calculateTotal(null); // ERROR: Argument of type 'null' is not assignable

Bug wird entdeckt: Beim Tippen. Nicht in Production.

2. Autocomplete auf Steroiden

JavaScript:

// Du musst raten, was das User-Objekt hat
function greetUser(user) {
  // user. ← Was ist hier verfügbar?
  // Dokumentation checken? Code durchsuchen?
  return `Hello ${user.name}`;
}

TypeScript:

interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
  lastLogin?: Date;
}

function greetUser(user: User) {
  // user. ← VS Code zeigt ALLE verfügbaren Properties!
  // ↓ id, name, email, role, lastLogin
  return `Hello ${user.name}`;
}

Resultat: Ich schreibe Code 3x schneller weil ich nicht ständig nachschauen muss.

3. Refactoring ohne Angst

Das JavaScript-Dilemma:

// Ich benenne diese Funktion um
function getUserData(id) { /* ... */ }

// Habe ich alle Stellen gefunden?
// Ctrl+F hilft nur bedingt...
// Test hoffentlich vorhanden?

TypeScript macht es sicher:

function getUserProfile(id: number): UserProfile {
  // ...
}

// Refactor → Rename Symbol (F2 in VS Code)
// TypeScript findet ALLE Verwendungen
// Inklusive:
// - Imports
// - Tests
// - Andere Files
// Garantiert nichts vergessen!

4. Selbst-dokumentierender Code

JavaScript:

/**
 * Erstellt einen neuen User
 * @param {Object} data - User Daten
 * @param {string} data.name - Name des Users
 * @param {string} data.email - Email des Users
 * @param {number} [data.age] - Alter (optional)
 * @returns {Promise<Object>} Der erstellte User
 */
async function createUser(data) {
  // ...
}

// Problem: Kommentare werden veraltet!
// Niemand updated sie bei Changes

TypeScript:

interface CreateUserInput {
  name: string;
  email: string;
  age?: number; // ? bedeutet optional
}

interface User extends CreateUserInput {
  id: number;
  createdAt: Date;
}

async function createUser(data: CreateUserInput): Promise<User> {
  // Der Code dokumentiert sich selbst!
  // TypeScript erzwingt, dass es korrekt bleibt
}

5. Vermeidung häufiger JavaScript-Fallen

Typo-Fehler:

interface Config {
  apiUrl: string;
  timeout: number;
}

const config: Config = {
  apiUrl: 'https://api.example.com',
  timeOut: 5000 // Error: Object literal may only specify known properties
};

// TypeScript catchet den Typo sofort!

Null/Undefined Safety:

// strictNullChecks aktiviert
function getUser(id: number): User | null {
  // ...
}

const user = getUser(123);
user.name; // Error: Object is possibly 'null'

// Du MUSST null/undefined handeln:
if (user) {
  user.name; // OK!
}

// Oder Optional Chaining:
const name = user?.name;

Falsche Return Types:

function calculateAge(birthYear: number): number {
  return new Date().getFullYear() - birthYear;
  // return "25"; // Error: Type 'string' is not assignable to type 'number'
}

6. Bessere IDE-Integration

VS Code mit TypeScript ist wie Magie:

// Hover über Funktion → Signature anzeigen
// Ctrl+Click → Gehe zu Definition
// F12 → Gehe zu Type Definition
// Shift+F12 → Finde alle Referenzen
// Ctrl+. → Quick Fix Vorschläge

// IntelliSense zeigt:
// - Parameter-Namen
// - Return Types
// - JSDoc Kommentare
// - Verwendungsbeispiele

7. Generics: Typsichere Wiederverwendbarkeit

// Ohne Generics (JavaScript Stil):
function getFirstElement(arr) {
  return arr[0]; // Was kommt zurück?
}

// Mit Generics:
function getFirstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

const nums = [1, 2, 3];
const first = getFirstElement(nums); // TypeScript weiß: 'first' ist number | undefined

const names = ['Max', 'Anna'];
const firstName = getFirstElement(names); // TypeScript weiß: 'firstName' ist string | undefined

Generics mit React:

// Typsichere Custom Hooks
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(initialValue);

  // TypeScript versteht den Type automatisch:
  return [value, setValue] as const;
}

// Usage:
const [user, setUser] = useLocalStorage<User>('user', { name: 'Max', id: 1 });
// user ist vom Type User!
// setUser akzeptiert nur User-Objekte!

8. Union Types und Type Guards

type Status = 'loading' | 'success' | 'error';

function handleStatus(status: Status) {
  // TypeScript kennt alle möglichen Werte
  if (status === 'loadiing') { // Error: Typo!
    // ...
  }
}

// Discriminated Unions:
type ApiResponse<T> =
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error };

function handleResponse(response: ApiResponse<User>) {
  if (response.status === 'success') {
    // TypeScript weiß: 'data' ist verfügbar!
    console.log(response.data.name);
  }

  if (response.status === 'error') {
    // TypeScript weiß: 'error' ist verfügbar!
    console.log(response.error.message);
  }
}

9. Utility Types: TypeScript’s Superpowers

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// Partial: Alle Props optional
type UpdateUser = Partial<User>;
// { id?: number; name?: string; email?: string; password?: string }

// Pick: Nur bestimmte Props
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }

// Omit: Bestimmte Props ausschließen
type UserPublic = Omit<User, 'password'>;
// { id: number; name: string; email: string }

// Readonly: Unveränderlich
type ImmutableUser = Readonly<User>;
// Alle Props sind readonly

// Record: Key-Value Map
type UserMap = Record<number, User>;
// { [key: number]: User }

10. Migration ist einfacher als du denkst

Du musst nicht alles auf einmal migrieren!

# 1. TypeScript hinzufügen
npm install -D typescript @types/node @types/react

# 2. tsconfig.json erstellen
npx tsc --init

# 3. Aktiviere 'allowJs'
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false, // Noch keine Errors in .js Files
    // ...
  }
}

# 4. Benenne Files schrittweise um
# user.js → user.ts
# Schritt für Schritt migrieren!

Die Kosten von TypeScript

Sei ehrlich, gibt es Nachteile?

Ja, ein paar:

  1. Lernkurve: 1-2 Wochen um die Basics zu lernen
  2. Setup: Initial Configuration nötig
  3. Build Step: TypeScript muss kompiliert werden
  4. Boilerplate: Manchmal mehr Code

ABER: Diese Kosten zahlen sich 10x aus durch:

  • Weniger Bugs
  • Schnelleres Development
  • Bessere Wartbarkeit
  • Selbstdokumentierender Code

Mein Fazit

Nach 3 Jahren TypeScript:

Vorher (JavaScript):

  • Stunden mit Debugging verbracht
  • Angst vor Refactoring
  • Ständig Dokumentation checken
  • Bugs in Production

Nachher (TypeScript):

  • Bugs bei Compile-Time gefunden
  • Refactoring mit Zuversicht
  • IDE sagt mir alles
  • Weniger Production Bugs

TypeScript ist nicht perfekt, aber es macht mich zu einem besseren Developer.

Ressourcen zum Lernen

Dein nächster Schritt

Starte klein:

  1. Erstelle eine neue utils.ts Datei
  2. Schreibe eine typsichere Helper-Funktion
  3. Fühle die Power von Autocomplete
  4. Du wirst nicht zurückwollen!

Nutzt du schon TypeScript? Was hält dich noch zurück? Lass es mich wissen!