Fortgeschritten122025-01-15

TypeScript Union & Intersection Types

Lerne Union Types (|) und Intersection Types (&) für flexible und präzise Typdefinitionen.

#typescript#union#intersection#types

TypeScript Union & Intersection Types

Union und Intersection Types ermöglichen flexible Typkombinationen.

Union Types (|)

Ein Wert kann einer von mehreren Typen sein:

let value: string | number

value = "Hello" // ✅ OK
value = 42 // ✅ OK
value = true // ❌ Error

Function Parameters

function format(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase()
  }
  return value.toFixed(2)
}

format("hello") // "HELLO"
format(42.567) // "42.57"

Literal Union Types

type Status = "active" | "inactive" | "pending"

function setStatus(status: Status): void {
  console.log(`Status: ${status}`)
}

setStatus("active") // ✅ OK
setStatus("deleted") // ❌ Error: not in union

Verwendung:

  • State Management
  • API Status Codes
  • Enum-Alternativen

Arrays mit mehreren Typen

let mixed: (string | number)[] = ["Max", 25, "Anna", 30]

// NICHT verwechseln mit:
let tuple: [string, number] = ["Max", 25] // Feste Reihenfolge

Intersection Types (&)

Ein Wert muss alle Typen erfüllen:

interface Person {
  name: string
  age: number
}

interface Employee {
  employeeId: number
  department: string
}

type EmployeePerson = Person & Employee

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

Mixin Pattern

interface Loggable {
  log(): void
}

interface Serializable {
  serialize(): string
}

class User implements Loggable, Serializable {
  constructor(public name: string) {}

  log(): void {
    console.log(this.name)
  }

  serialize(): string {
    return JSON.stringify(this)
  }
}

type LoggableSerializable = Loggable & Serializable

const user: LoggableSerializable = new User("Max")
user.log()
console.log(user.serialize())

Type Guards

Typprüfung zur Laufzeit:

typeof Guard

function process(value: string | number) {
  if (typeof value === "string") {
    // value ist hier Type: string
    return value.toUpperCase()
  }
  // value ist hier Type: number
  return value.toFixed(2)
}

instanceof Guard

class Dog {
  bark() {
    console.log("Woof!")
  }
}

class Cat {
  meow() {
    console.log("Meow!")
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark()
  } else {
    animal.meow()
  }
}

in Operator

interface Car {
  drive(): void
}

interface Boat {
  sail(): void
}

function move(vehicle: Car | Boat) {
  if ("drive" in vehicle) {
    vehicle.drive()
  } else {
    vehicle.sail()
  }
}

Custom Type Guards

interface Fish {
  swim(): void
}

interface Bird {
  fly(): void
}

// Type Predicate
function isFish(animal: Fish | Bird): animal is Fish {
  return (animal as Fish).swim !== undefined
}

function move(animal: Fish | Bird) {
  if (isFish(animal)) {
    animal.swim()
  } else {
    animal.fly()
  }
}

Discriminated Unions

Union Types mit gemeinsamer Property:

interface Circle {
  kind: "circle"
  radius: number
}

interface Rectangle {
  kind: "rectangle"
  width: number
  height: number
}

interface Triangle {
  kind: "triangle"
  base: number
  height: number
}

type Shape = Circle | Rectangle | Triangle

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2
    case "rectangle":
      return shape.width * shape.height
    case "triangle":
      return (shape.base * shape.height) / 2
  }
}

Vorteile:

  • Exhaustiveness Checking
  • Type Narrowing
  • Keine Type Assertions nötig

Exhaustiveness Checking

type Shape = Circle | Rectangle | Triangle

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2
    case "rectangle":
      return shape.width * shape.height
    case "triangle":
      return (shape.base * shape.height) / 2
    default:
      const _exhaustive: never = shape
      return _exhaustive
  }
}
// Wenn Shape erweitert wird, gibt's Compiler-Error!

Union vs Intersection

Wann Union (|)

// "Entweder/Oder"
type PaymentMethod = "credit_card" | "paypal" | "bank_transfer"

// Mehrere mögliche Return Types
function getData(id: number): User | null {
  // ...
}

// Flexible Parameter
function print(value: string | number | boolean): void {
  console.log(value)
}

Wann Intersection (&)

// "Kombiniere beide"
type Admin = User & {
  permissions: string[]
}

// Mixins
type Timestamped = {
  createdAt: Date
  updatedAt: Date
}

type User = {
  name: string
  email: string
}

type TimestampedUser = User & Timestamped
Real-World API Response Types
// Base Response
interface ApiResponse {
  status: number
  timestamp: Date
}

// Success Response
interface SuccessResponse<T> extends ApiResponse {
  success: true
  data: T
}

// Error Response
interface ErrorResponse extends ApiResponse {
  success: false
  error: string
  code: string
}

// Union Type für Response
type Response<T> = SuccessResponse<T> | ErrorResponse

// Usage
async function fetchUser(id: number): Promise<Response<User>> {
  try {
    const data = await api.get(`/users/${id}`)
    return {
      success: true,
      data,
      status: 200,
      timestamp: new Date()
    }
  } catch (error) {
    return {
      success: false,
      error: error.message,
      code: "USER_NOT_FOUND",
      status: 404,
      timestamp: new Date()
    }
  }
}

// Type Guard
const response = await fetchUser(1)
if (response.success) {
  console.log(response.data.name) // Type: User
} else {
  console.error(response.error) // Type: string
}

📝 Quiz

Was ist der Unterschied zwischen Union (|) und Intersection (&)?

Tipps & Tricks

Type Narrowing mit Control Flow

function process(value: string | number | null) {
  if (value === null) {
    return // value ist null
  }

  if (typeof value === "string") {
    return value.toUpperCase() // value ist string
  }

  return value.toFixed(2) // value ist number
}

Optional vs Union mit undefined

// Identisch
interface User {
  name: string
  email?: string
}

interface User {
  name: string
  email: string | undefined
}

// Aber unterschiedlich bei Objects:
const user1: User = { name: "Max" } // ✅ OK
const user2: User = { name: "Max", email: undefined } // ✅ OK

Branded Types

type UserId = string & { readonly brand: unique symbol }
type ProductId = string & { readonly brand: unique symbol }

function getUserById(id: UserId): User {
  // ...
}

const userId = "123" as UserId
const productId = "456" as ProductId

getUserById(userId) // ✅ OK
getUserById(productId) // ❌ Error

Häufige Fehler

Fehler 1: Intersection mit widersprüchlichen Types

FALSCH:

type Never = string & number // Type: never
// Kann nicht string UND number sein!

RICHTIG:

type StringOrNumber = string | number // Union statt Intersection

Fehler 2: Type Guards vergessen

FALSCH:

function process(value: string | number) {
  return value.toUpperCase() // ❌ Error: number hat kein toUpperCase
}

RICHTIG:

function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase()
  }
  return value.toFixed(2)
}

Fehler 3: Discriminated Union ohne gemeinsames Field

FALSCH:

interface Success {
  data: string
}

interface Error {
  error: string
}

type Result = Success | Error

function handle(result: Result) {
  if (result.data) { // ❌ Error: data existiert nicht auf Error
    // ...
  }
}

RICHTIG:

interface Success {
  success: true
  data: string
}

interface Error {
  success: false
  error: string
}

type Result = Success | Error

function handle(result: Result) {
  if (result.success) {
    console.log(result.data) // ✅ OK
  } else {
    console.log(result.error) // ✅ OK
  }
}
🎯

Zusammenfassung

Du hast gelernt:

  • ✅ Union Types (|) für ODER-Verknüpfungen
  • ✅ Intersection Types (&) für UND-Verknüpfungen
  • ✅ Type Guards für Runtime-Checks
  • ✅ Discriminated Unions mit gemeinsamer Property
  • ✅ Exhaustiveness Checking
  • ✅ Custom Type Guards mit is

Key Takeaways:

  • Union = einer von mehreren
  • Intersection = alle kombiniert
  • Discriminated Unions für Type Safety
  • Type Guards für Type Narrowing
  • never Type für unmögliche Kombinationen

Nächste Schritte

Als Nächstes lernst du:

  • Utility Types (Pick, Omit, Partial, etc.)
  • Mapped Types
  • Conditional Types
  • Template Literal Types

Viel Erfolg! 🚀

TypeScriptLektion 7 von 15
47% abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten