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
Related Topics
- 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