Skip to main content

Type Inference

Definition

Type Inference (Typinferenz) ist die Fähigkeit von Swift, den Typ einer Variable oder Konstante automatisch zu erkennen, ohne dass dieser explizit angegeben werden muss. Der Swift-Compiler analysiert den zugewiesenen Wert und leitet daraus den korrekten Typ ab.

Vorteil: Weniger Boilerplate-Code bei gleichzeitiger Type Safety

Basic Syntax

// Explizite Typ-Angabe
let explicitString: String = "Hello"
let explicitInt: Int = 42
let explicitDouble: Double = 3.14

// Type Inference - Compiler erkennt Typ automatisch
let inferredString = "Hello" // String
let inferredInt = 42 // Int
let inferredDouble = 3.14 // Double
let inferredBool = true // Bool

Grundlegende Typen

// Integer
let smallNumber = 5 // Int
let bigNumber = 1_000_000 // Int

// Double vs Float
let pi = 3.14159 // Double (Standard für Dezimalzahlen)
let piFloat: Float = 3.14159 // Explizit Float angeben

// String
let name = "Max" // String
let multiline = """
Mehrzeiliger
String
""" // String

// Boolean
let isActive = true // Bool
let hasPermission = false // Bool

// Character (benötigt explizite Typ-Angabe)
let letter: Character = "A" // Ohne Typ-Angabe wäre es String

Collections mit Type Inference

// Arrays
let numbers = [1, 2, 3, 4, 5] // Array<Int>
let names = ["Anna", "Bob", "Charlie"] // Array<String>
let mixed: [Any] = [1, "two", 3.0] // Explizit nötig für gemischte Typen

// Dictionaries
let scores = ["Anna": 95, "Bob": 87] // Dictionary<String, Int>
let ages = ["Anna": 25, "Bob": 30] // Dictionary<String, Int>

// Sets
let uniqueNumbers = Set([1, 2, 3, 3, 4]) // Set<Int>
let fruits = Set(["Apple", "Banana"]) // Set<String>

// Leere Collections (Typ-Angabe erforderlich)
let emptyArray: [Int] = []
let emptyDict: [String: Int] = [:]
let emptySet: Set<String> = []

// Oder mit Type Inference durch Kontext
var anotherEmptyArray = [Int]() // Array<Int>
var anotherEmptyDict = [String: Int]() // Dictionary<String, Int>
var anotherEmptySet = Set<String>() // Set<String>

Optionals

// Optional mit Type Inference
let possibleNumber = Int("123") // Int? (Optional<Int>)
let possibleName: String? = nil // Explizit nötig bei nil

// Optional Binding
if let number = possibleNumber { // Type wird von possibleNumber abgeleitet
print("Number is \(number)") // number ist Int (nicht Int?)
}

// Nil-Coalescing
let name: String? = nil
let displayName = name ?? "Guest" // String (nicht Optional)

Functions und Closures

// Funktion mit Type Inference
func add(_ a: Int, _ b: Int) -> Int {
return a + b // Return-Typ wird aus Signatur abgeleitet
}

// Implicit Return
func multiply(_ a: Int, _ b: Int) -> Int {
a * b // Return-Keyword optional bei einzeiligen Funktionen
}

// Closure mit Type Inference
let numbers = [1, 2, 3, 4, 5]

// Vollständige Typ-Angabe
let doubled1 = numbers.map({ (number: Int) -> Int in
return number * 2
})

// Type Inference - Compiler kennt Input und Output
let doubled2 = numbers.map({ number in
return number * 2
})

// Noch kompakter
let doubled3 = numbers.map({ $0 * 2 })

// Trailing Closure
let doubled4 = numbers.map { $0 * 2 }

Generics und Type Inference

// Generic Funktion
func printValue<T>(_ value: T) {
print(value)
}

// Type wird automatisch erkannt
printValue(42) // T ist Int
printValue("Hello") // T ist String
printValue(3.14) // T ist Double

// Generic Structures
struct Stack<Element> {
private var items: [Element] = []

mutating func push(_ item: Element) {
items.append(item)
}

mutating func pop() -> Element? {
items.popLast()
}
}

// Type Inference beim Initialisieren
var intStack = Stack<Int>() // Explizit
var stringStack = Stack<String>() // Explizit

// Mit Type Inference durch erste Operation
var inferredStack = Stack()
inferredStack.push(1) // Stack<Int>
inferredStack.push(2)

SwiftUI mit Type Inference

// View Body
struct ContentView: View {
var body: some View { // Return-Typ durch Protocol definiert
VStack { // VStack-Typ wird inferiert
Text("Hello") // Text-Typ wird inferiert
Button("Tap") { // Button-Typ wird inferiert
print("Tapped")
}
}
}
}

// @State Property
@State private var counter = 0 // Int
@State private var name = "" // String
@State private var isOn = false // Bool

// Binding
TextField("Name", text: $name) // Binding<String> wird inferiert

Enum Cases mit Associated Values

enum Result {
case success(String)
case failure(Error)
}

// Type Inference bei Pattern Matching
let result = Result.success("Done")

switch result {
case .success(let message): // message ist String
print(message)
case .failure(let error): // error ist Error
print(error)
}

// Mit if-case
if case .success(let message) = result {
print(message) // message ist String
}

Tuple Type Inference

// Tuple mit Type Inference
let person = ("Max", 25) // (String, Int)
let coordinates = (x: 10, y: 20) // (x: Int, y: Int)

// Destructuring
let (name, age) = person // name: String, age: Int
print("\(name) is \(age) years old")

// Accessing
let x = coordinates.x // Int
let y = coordinates.y // Int

Type Inference mit nil

// Fehler: Compiler kann Typ nicht inferieren
// let unknown = nil // Error!

// Explizite Typ-Angabe erforderlich
let nothing: String? = nil // String?

// Oder durch Kontext
let optionalName: String? = nil
let name = optionalName ?? "Guest" // String (nicht Optional)

Kontextuelle Type Inference

// Enum Cases
enum Direction {
case north, south, east, west
}

func move(in direction: Direction) {
print("Moving \(direction)")
}

// Vollständige Schreibweise
move(in: Direction.north)

// Kontextuelle Type Inference - Compiler kennt erwarteten Typ
move(in: .north) // .north wird als Direction.north erkannt

// In Switch Statements
let direction = Direction.north

switch direction {
case .north: // Direction.north
print("Going north")
case .south: // Direction.south
print("Going south")
case .east, .west: // Direction.east, Direction.west
print("Going east or west")
}

Higher-Order Functions

let numbers = [1, 2, 3, 4, 5]

// map
let doubled = numbers.map { $0 * 2 } // [Int]

// filter
let evens = numbers.filter { $0 % 2 == 0 } // [Int]

// reduce
let sum = numbers.reduce(0) { $0 + $1 } // Int

// compactMap
let strings = ["1", "2", "three", "4"]
let validNumbers = strings.compactMap { Int($0) } // [Int]

// flatMap
let nested = [[1, 2], [3, 4], [5, 6]]
let flattened = nested.flatMap { $0 } // [Int]

Custom Types

struct User {
let name: String
let age: Int
}

// Type Inference beim Initialisieren
let user = User(name: "Max", age: 25) // User

// In Functions
func greet(_ user: User) {
print("Hello, \(user.name)")
}

greet(user) // Compiler kennt Typ

// In Arrays
let users = [
User(name: "Anna", age: 25),
User(name: "Bob", age: 30)
] // [User]

Async/Await

// Async Function
func fetchData() async -> String {
return "Data" // Return-Typ wird inferiert
}

// Usage
Task {
let data = await fetchData() // data ist String
print(data)
}

// Throwing Functions
func loadUser() async throws -> User {
// Load user
}

Task {
do {
let user = try await loadUser() // user ist User
print(user.name)
} catch {
print(error) // error ist Error
}
}

Property Wrappers

// @Published
class ViewModel: ObservableObject {
@Published var count = 0 // Int
@Published var name = "" // String
@Published var users: [User] = [] // [User]
}

// @State
struct ContentView: View {
@State private var text = "" // String
@State private var isOn = false // Bool
@State private var counter = 0 // Int

var body: some View {
Text(text) // Type wird inferiert
}
}

Best Practices

✅ Verwende Type Inference

// Gut - Typ ist offensichtlich
let name = "Max"
let age = 25
let numbers = [1, 2, 3, 4, 5]

✅ Explizite Typen bei Unklarheit

// Gut - Typ ist nicht sofort ersichtlich
let ratio: Double = 3 / 4 // Ohne Typ-Angabe wäre es 0 (Int-Division)
let float: Float = 3.14 // Sonst würde Double inferiert
let char: Character = "A" // Sonst würde String inferiert

✅ Explizite Typen bei nil

// Gut - Erforderlich bei nil
let optionalName: String? = nil
let optionalAge: Int? = nil

✅ Explizite Typen für bessere Dokumentation

// Gut - Macht Intent deutlich
let timeout: TimeInterval = 30
let maxRetries: Int = 3
let baseURL: URL? = URL(string: "https://api.example.com")

❌ Unnötige Typ-Angaben

// Schlecht - Redundant
let name: String = "Max"
let age: Int = 25
let pi: Double = 3.14

Type Inference Limitations

// Compiler kann Typ nicht inferieren
// let unknown = nil // Error!
let typed: String? = nil // OK

// Mehrdeutige Fälle
// let number = 3 / 4 // Int? Oder Double?
let intResult = 3 / 4 // 0 (Int)
let doubleResult = 3.0 / 4.0 // 0.75 (Double)
let explicitDouble: Double = 3 / 4 // 0.75 (Double)

// Leere Collections
// let empty = [] // Error!
let emptyInts: [Int] = [] // OK
let emptyWithInference = [Int]() // OK

Common Use Cases

  • Variable Deklarationen: Weniger Code, gleiche Safety
  • Function Returns: Compiler leitet Return-Typ ab
  • Closures: Kompaktere Syntax durch Type Inference
  • Collections: Arrays, Dictionaries, Sets
  • SwiftUI: Views und Property Wrappers
  • Pattern Matching: Switch und if-case Statements
  • Generics: Automatische Typ-Ableitung
  • Closures: Type Inference macht Closures kompakter
  • Generics: Arbeitet Hand in Hand mit Type Inference
  • Optionals: Type Inference bei Optional Binding
  • SwiftUI: Extensive Verwendung von Type Inference