Skip to content

Types and Type Inference

Swift is a strongly typed language with a rich type system, supporting value types (structs, enums) and reference types (classes). Type inference minimizes explicit type declarations while maintaining safety.

Built-in Types

Swift provides a comprehensive set of built-in types.

Numeric Types

  • Integers: Int (platform-dependent, typically 64-bit), UInt, Int8, Int16, Int32, Int64.
  • Floating-Point: Double (64-bit), Float (32-bit).

Example:

swift
let count: Int = 42
let price: Double = 9.99
let smallValue: Int8 = 127 // Max for Int8

Boolean Type

  • Bool: true or false.

Example:

swift
let isActive: Bool = true

Text Types

  • Character: Single Unicode grapheme cluster.
  • String: Collection of characters.

Example:

swift
let letter: Character = "A"
let greeting: String = "Hello, Swift!"

Collection Types

  • Array: Ordered list.
  • Dictionary: Key-value pairs.
  • Set: Unordered unique elements.

Example:

swift
let numbers: [Int] = [1, 2, 3]
let scores: [String: Int] = ["Alice": 90]
let colors: Set<String> = ["Red", "Blue"]

Tuple Type

Groups multiple values, optionally named.

Example:

swift
let point: (x: Double, y: Double) = (1.0, 2.0)
let status: (Int, String) = (200, "OK")
print(point.x, status.0) // 1.0, 200

Function Types

Represent function signatures.

Example:

swift
let operation: (Int, Int) -> Int = (+)
print(operation(5, 3)) // 8

Type Aliases

Define alternative names for types.

Example:

swift
typealias Coordinate = (Double, Double)
typealias JSON = [String: Any]
let origin: Coordinate = (0.0, 0.0)

Nested Types

Define types within structs, classes, or enums.

Example:

swift
struct Card {
    enum Suit: String {
        case spades, hearts, diamonds, clubs
    }
    struct Rank {
        let value: Int
    }
    
    let suit: Suit
    let rank: Rank
}

let aceOfSpades = Card(suit: .spades, rank: .init(value: 1))

Type Inference

Swift infers types from initial values, function return types, or context.

Example: Variable Inference:

swift
let count = 10 // Inferred as Int
let name = "Swift" // Inferred as String
let values = [1.0, 2.0] // Inferred as [Double]

Example: Function Inference:

swift
func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}
let result = add(2, 3) // Inferred as Int

Example: Closure Inference:

swift
let sorted = [3, 1, 2].sorted { $0 < $1 } // Inferred as [Int]

Type Safety

Swift enforces type correctness at compile time.

Example:

swift
let number: Int = 42
// number = "Hello" // Error: Cannot assign value of type 'String' to type 'Int'

Type Casting

Use as, as?, or as! for type conversion.

Example:

swift
let value: Any = 42
if let intValue = value as? Int {
    print("Integer: \(intValue)")
}
let forced = value as! Int // Crashes if not Int

Type Erasure

Hide specific types while preserving protocol conformance.

Example:

swift
protocol Shape {
    func draw()
}

struct AnyShape: Shape {
    private let _draw: () -> Void
    init<T: Shape>(_ shape: T) {
        _draw = shape.draw
    }
    func draw() { _draw() }
}

let shapes: [AnyShape] = [AnyShape(Circle()), AnyShape(Rectangle())]

Metatype Types

Refer to the type itself using .Type or .self.

Example:

swift
let type: String.Type = String.self
let instance = type.init("Hello")
print(instance) // "Hello"

Best Practices

  • Specific Types: Use Int32 or Float only when required (e.g., interop).
  • Type Inference: Rely on inference for local variables, annotate for clarity in APIs.
  • Type Aliases: Use for complex types to improve readability.
  • Type Safety: Avoid Any unless necessary (e.g., JSON parsing).
  • Nested Types: Organize related types within parent types.
  • Type Casting: Prefer conditional casting (as?) over forced (as!).

Troubleshooting

  • Type Mismatch: Check inferred vs. expected types.
  • Ambiguous Inference: Add type annotations to resolve conflicts.
  • Casting Failures: Verify types with is before casting.
  • Performance Issues: Minimize use of Any or dynamic typing.

Example: Comprehensive Type Usage

swift
typealias Point = (x: Double, y: Double)

struct Geometry {
    enum ShapeType {
        case circle(radius: Double)
        case rectangle(width: Double, height: Double)
    }
    
    static let origin: Point = (0.0, 0.0)
    
    var shapes: [AnyShape]
    var coordinates: [Point]
    
    func addShape(_ shape: ShapeType) -> String {
        switch shape {
        case let .circle(radius):
            return "Circle with radius \(radius)"
        case let .rectangle(width, height):
            return "Rectangle with area \(width * height)"
        }
    }
}

let geometry = Geometry(shapes: [], coordinates: [(1.0, 2.0)])
print(geometry.addShape(.circle(radius: 5.0))) // "Circle with radius 5.0"

Released under the MIT License.