Type Inference in Swift: How the Compiler Reads Your Mind
You’ve probably noticed something curious: when you write let movieTitle = "Toy Story", you never explicitly say it’s
a String. Yet Swift knows. It figures out the type from the value you assign — and it does this at compile time, with
zero performance cost. This ability is called type inference.
In this guide, you’ll learn how type inference works, when to let Swift figure things out, and when you should add an explicit type annotation instead. We won’t cover generics or protocol associated types — those build on this foundation in later posts.
This guide assumes you’re familiar with data types.
What You’ll Learn
- What Is Type Inference?
- How It Works
- Type Annotations
- When to Add Annotations
- Inference with Expressions
- Common Mistakes
- What’s Next?
What Is Type Inference?
Type inference is the compiler’s ability to figure out what type a value is without you spelling it out. Instead of
writing let name: String = "Buzz", you can just write let name = "Buzz" and Swift deduces that it’s a String.
Imagine you’re sorting the mail at Monsters, Inc. If someone hands you a scream canister, you don’t need them to say “this is a scream canister” — you can see what it is. Swift works the same way: it looks at the value and infers the type.
The key thing to understand: type inference happens at compile time, not at runtime. Your code is just as safe and just as fast as if you wrote every type explicitly.
How It Works
When you assign a value, Swift examines the right side of the = and determines the type:
let movie = "Ratatouille" // String
let year = 2007 // Int
let rating = 8.0 // Double
let isPixar = true // Bool
Swift infers each type from the literal value:
- Text in quotes →
String - Whole number →
Int - Number with decimal →
Double trueorfalse→Bool
You can verify the inferred type using type(of:):
let score = 9.5
print(type(of: score))
Double
Note: Type inference only happens at the moment of declaration. Once Swift decides a variable’s type, it cannot change — the variable is still strongly typed.
Type Annotations
A type annotation is when you explicitly tell Swift what type a value should be. You add it after the name with a colon:
let title: String = "Inside Out"
let releaseYear: Int = 2015
let audienceScore: Double = 94.0
let isSequel: Bool = false
This is functionally identical to letting Swift infer the types. The annotation is optional here because the values already make the types obvious.
When to Add Annotations
Most of the time, you can skip type annotations and let Swift infer. But there are situations where an annotation is helpful or even required:
When There’s No Initial Value
If you declare a variable without assigning a value immediately, Swift has nothing to infer from:
var upcomingMovie: String
upcomingMovie = "Toy Story 5"
print(upcomingMovie)
Toy Story 5
Without the : String annotation, Swift would report an error because it can’t guess what type you intend.
When You Want a Different Type Than the Default
Sometimes Swift’s default inference isn’t what you need:
let defaultInferred = 8.0 // Double (Swift's default)
let asFloat: Float = 8.0 // Float (explicit override)
print(type(of: defaultInferred))
print(type(of: asFloat))
Double
Float
Swift infers 8.0 as Double by default. If you need a Float, you must say so explicitly.
For Clarity in Complex Code
Even when inference works, an annotation can make your code more readable:
let maxRetries: Int = 3
let timeoutSeconds: Double = 30.0
These annotations aren’t required, but they make the intent crystal clear to anyone reading the code.
Tip: A good rule of thumb: omit the annotation when the type is obvious from the value. Add it when the type isn’t immediately clear or when you need a non-default type.
Inference with Expressions
Type inference also works when the value comes from an expression, not just a literal:
let firstName = "Buzz"
let lastName = "Lightyear"
let fullName = firstName + " " + lastName
print(type(of: fullName))
String
Swift sees that firstName and lastName are both strings, so adding them produces another String. The same logic
applies to numeric operations:
let baseScore = 80
let bonus = 15
let totalScore = baseScore + bonus
print(type(of: totalScore))
print(totalScore)
Int
95
Note: This is why you can’t add an
Intand aDoublewithout converting one — Swift infers each side’s type and they must match.
Common Mistakes
Assuming Inference Means “No Type”
// ❌ Misunderstanding: "movie has no type"
var movie = "Wall-E"
// movie = 42 // Error: cannot assign Int to String
// ✅ Understanding: inference assigns a permanent type
var movie = "Wall-E"
movie = "Cars" // Works — still a String
print(movie)
Cars
Type inference doesn’t mean the variable is typeless. It means Swift assigns the type for you. Once assigned, the type is locked — just as strict as if you wrote it explicitly.
Forgetting Annotations on Empty Declarations
// ❌ Won't compile — nothing to infer from
// var characterName
// characterName = "Woody"
// ✅ Add a type annotation when there's no initial value
var characterName: String
characterName = "Woody"
print(characterName)
Woody
If you don’t assign a value on the same line, Swift needs a type annotation to know what to expect.
What’s Next?
- Type inference lets Swift deduce types from values
- It happens at compile time — no performance cost
- Add type annotations when there’s no initial value, when you need a non-default type, or for clarity
- Inferred types are permanent — a
Stringstays aString - Type inference keeps your code clean without sacrificing safety
Now that you understand how Swift handles types, it’s time to learn how to work with values using operators. Head over to Swift Operators to explore arithmetic, comparison, and logical operators.