Fortgeschritten142025-01-15

TypeScript Interfaces - Objekt-Typen definieren

Lerne wie du mit Interfaces komplexe Objekt-Strukturen typisierst. Optional Properties, Readonly, Extending und mehr.

#typescript#interfaces#objects#oop

TypeScript Interfaces - Objekt-Typen definieren

Interfaces sind das Herzstück von TypeScript für Objekt-Typisierung.

Was sind Interfaces?

Ein Interface definiert die Struktur eines Objekts:

interface User {
  name: string
  age: number
  email: string
}

// Objekt muss exakt der Struktur entsprechen
const user: User = {
  name: "Max",
  age: 25,
  email: "max@example.com"
}

Vorteile:

  • ✅ Autovervollständigung
  • ✅ Type-Checking
  • ✅ Refactoring-sicher
  • ✅ Selbstdokumentierend

Interface Basics

Einfaches Interface

interface Product {
  id: number
  name: string
  price: number
  inStock: boolean
}

function displayProduct(product: Product): void {
  console.log(`${product.name}: ${product.price}€`)
}

const laptop: Product = {
  id: 1,
  name: "MacBook Pro",
  price: 1999,
  inStock: true
}

displayProduct(laptop)

Optional Properties

Mit ? sind Properties optional:

interface User {
  name: string
  age: number
  email?: string // Optional!
  phone?: string // Optional!
}

// Beide OK
const user1: User = { name: "Max", age: 25 }
const user2: User = { name: "Anna", age: 30, email: "anna@test.com" }

Readonly Properties

Mit readonly sind Properties unveränderlich:

interface Config {
  readonly apiUrl: string
  readonly timeout: number
  retries: number // Normal, änderbar
}

const config: Config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3
}

config.retries = 5 // ✅ OK
config.apiUrl = "https://other.com" // ❌ Error: readonly!

Function Properties

Interfaces können auch Funktionen enthalten:

Method Syntax

interface Calculator {
  add(a: number, b: number): number
  subtract(a: number, b: number): number
}

const calc: Calculator = {
  add(a, b) {
    return a + b
  },
  subtract(a, b) {
    return a - b
  }
}

Property Syntax

interface Calculator {
  add: (a: number, b: number) => number
  subtract: (a: number, b: number) => number
}

Beide Syntaxen sind OK, Method Syntax ist üblicher.

Index Signatures

Für Objekte mit dynamischen Keys:

interface StringMap {
  [key: string]: string
}

const translations: StringMap = {
  hello: "Hallo",
  goodbye: "Auf Wiedersehen",
  thanks: "Danke"
  // Beliebig viele string: string Paare
}

translations.welcome = "Willkommen" // ✅ OK
translations.count = 42 // ❌ Error: value muss string sein

Index Signatures mit zusätzlichen Properties

interface Dictionary {
  [key: string]: string | number
  length: number // Muss kompatibel mit Index Signature sein
}

const dict: Dictionary = {
  length: 2,
  hello: "Hallo",
  count: 42
}

Extending Interfaces

Interfaces können von anderen Interfaces erben:

interface Person {
  name: string
  age: number
}

interface Employee extends Person {
  employeeId: number
  department: string
}

const employee: Employee = {
  name: "Max",
  age: 25,
  employeeId: 12345,
  department: "IT"
}

Multiple Extends

interface Printable {
  print(): void
}

interface Saveable {
  save(): void
}

interface Document extends Printable, Saveable {
  title: string
  content: string
}

const doc: Document = {
  title: "My Doc",
  content: "...",
  print() { console.log(this.title) },
  save() { console.log("Saved!") }
}

Interface vs Type Alias

TypeScript hat auch Type Aliases:

// Interface
interface User {
  name: string
  age: number
}

// Type Alias
type User = {
  name: string
  age: number
}

Unterschiede

| Feature | Interface | Type Alias | |---------|-----------|------------| | Extending | extends | & (Intersection) | | Merging | ✅ Ja | ❌ Nein | | Union Types | ❌ Nein | ✅ Ja | | Primitives | ❌ Nein | ✅ Ja | | Tuples | ⚠️ Umständlich | ✅ Ja |

Declaration Merging

Interfaces mit gleichem Namen werden gemergt:

interface User {
  name: string
}

interface User {
  age: number
}

// Beide werden zusammengefügt
const user: User = {
  name: "Max",
  age: 25
}

Vorteil: Gut für Library-Erweiterungen!

Type Alias für komplexe Typen

// Union Types - nur mit Type Alias
type Status = "active" | "inactive" | "pending"

// Intersection Types
type Admin = User & {
  permissions: string[]
}

// Primitive Aliases
type ID = string | number

// Tuples
type Point = [number, number]

Empfehlung

  • Interfaces für Objekt-Strukturen (besonders wenn extending nötig)
  • Type Aliases für Unions, Intersections, Tuples, Primitives
Interface vs Type - Best Practices
// ✅ Interface für Objekte
interface User {
  id: number
  name: string
}

interface Admin extends User {
  permissions: string[]
}

// ✅ Type Alias für Unions
type Status = "active" | "inactive" | "pending"

// ✅ Type Alias für Intersections
type AdminUser = User & {
  role: "admin"
}

// ✅ Type Alias für komplexe Typen
type ApiResponse<T> = {
  data: T
  status: number
  error?: string
}

Funktionen mit Interfaces

Call Signatures

Interface für Funktions-Typen:

interface MathOperation {
  (a: number, b: number): number
}

const add: MathOperation = (a, b) => a + b
const multiply: MathOperation = (a, b) => a * b

Construct Signatures

Für Constructor-Funktionen:

interface PersonConstructor {
  new (name: string, age: number): Person
}

interface Person {
  name: string
  age: number
}

class Employee implements Person {
  constructor(public name: string, public age: number) {}
}

// PersonConstructor Type für die Klasse
const PersonClass: PersonConstructor = Employee
const person = new PersonClass("Max", 25)

Hybrid Types

Interfaces können beides sein - Objekt UND Funktion:

interface Counter {
  (start: number): string // Callable
  interval: number // Property
  reset(): void // Method
}

function getCounter(): Counter {
  const counter = (start: number) => {
    return `Started at ${start}`
  } as Counter

  counter.interval = 1000
  counter.reset = () => {
    console.log("Reset!")
  }

  return counter
}

const c = getCounter()
c(10) // "Started at 10"
c.interval // 1000
c.reset() // "Reset!"

Interfaces für React Components

Sehr nützlich in React:

interface ButtonProps {
  text: string
  onClick: () => void
  disabled?: boolean
  variant?: "primary" | "secondary"
}

function Button({ text, onClick, disabled = false, variant = "primary" }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled} className={variant}>
      {text}
    </button>
  )
}

// Usage mit Autovervollständigung
<Button text="Click me" onClick={() => console.log("Clicked")} />

📝 Quiz

Was ist der Hauptunterschied zwischen Interface und Type Alias?

Tipps & Tricks

Interface Naming Convention

// ❌ Kein 'I' Prefix (wie in C#)
interface IUser {
  name: string
}

// ✅ Einfach den Namen
interface User {
  name: string
}

// ✅ Beschreibende Namen für Props
interface UserCardProps {
  user: User
  onClick: () => void
}

Pick und Omit Utilities

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

// Nur bestimmte Properties
type PublicUser = Pick<User, "id" | "name" | "email">

// Alle außer bestimmte
type UserWithoutPassword = Omit<User, "password">

Partial und Required

interface User {
  name: string
  age: number
  email: string
}

// Alle Properties optional
type PartialUser = Partial<User>

// Alle Properties required
interface OptionalUser {
  name?: string
  age?: number
}
type RequiredUser = Required<OptionalUser>

Häufige Fehler

Fehler 1: Excess Property Checks ignorieren

FALSCH:

interface User {
  name: string
  age: number
}

const user: User = {
  name: "Max",
  age: 25,
  email: "max@test.com" // ❌ Error: excess property
}

RICHTIG:

// Entweder email hinzufügen
interface User {
  name: string
  age: number
  email?: string
}

// Oder nicht als User typen
const user = {
  name: "Max",
  age: 25,
  email: "max@test.com"
}

Fehler 2: Readonly falsch verstehen

FALSCH:

interface User {
  readonly name: string
  readonly hobbies: string[]
}

const user: User = {
  name: "Max",
  hobbies: ["gaming", "coding"]
}

user.hobbies.push("reading") // ✅ OK! Array ist änderbar

RICHTIG:

interface User {
  readonly name: string
  readonly hobbies: readonly string[]
}

const user: User = {
  name: "Max",
  hobbies: ["gaming", "coding"]
}

user.hobbies.push("reading") // ❌ Error

Fehler 3: Interface vs Type Alias verwechseln

FALSCH:

// Union Type mit Interface nicht möglich
interface Status extends "active" | "inactive" // ❌ Error

RICHTIG:

// Union Type mit Type Alias
type Status = "active" | "inactive"
🎯

Zusammenfassung

Du hast gelernt:

  • ✅ Interfaces definieren Objekt-Strukturen
  • ✅ Optional Properties mit ?
  • ✅ Readonly Properties mit readonly
  • ✅ Interfaces erweitern mit extends
  • ✅ Index Signatures für dynamische Keys
  • ✅ Declaration Merging bei Interfaces
  • ✅ Interface vs Type Alias - wann was nutzen

Key Takeaways:

  • Interfaces für Objekte, Type Aliases für Unions/Intersections
  • readonly macht nur die Referenz unveränderlich, nicht den Inhalt
  • Excess Property Checks bei direkten Zuweisungen
  • Utility Types wie Pick, Omit, Partial nutzen

Nächste Schritte

Als Nächstes lernst du:

  • Classes in TypeScript
  • Generics für wiederverwendbare Interfaces
  • Advanced Types (Union, Intersection, etc.)

Viel Erfolg! 🚀

TypeScriptLektion 3 von 15
20% abgeschlossen
Lektion abgeschlossen!

Gut gemacht! 🎉

Du hast "TypeScript Interfaces - Objekt-Typen definieren" abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten