Types
Every value in Swift has a type, and the language has a small grammar for writing those types down. This reference summarizes the type syntax — annotations, named and compound types, optional and opaque forms, metatypes, and how type inference fills in the gaps.
Type annotations
A type annotation follows a colon after a name and states the type explicitly. It's optional wherever inference can determine the type from the value.
let count: Int = 0
var names: [String] = []
func greet(_ who: String) -> String { "Hi \(who)" }
Type identifiers
A type identifier names a type directly (Int) or
through dotted module/nesting qualification (Swift.Int,
Outer.Inner). Generic types are written with a generic argument
clause: Array<Int>, Dictionary<String, Int>.
A type alias introduces an alternate name for an existing type.
Compound types
Swift builds richer types out of simpler ones:
| Type | Syntax | Sugar for |
|---|---|---|
| Tuple | (Int, String), (x: Int, y: Int) | — |
| Function | (Int) -> Bool | — |
| Array | [Element] | Array<Element> |
| Dictionary | [Key: Value] | Dictionary<Key, Value> |
| Optional | Wrapped? | Optional<Wrapped> |
The empty tuple () is the type Void. Function types
may carry attributes such as @escaping, @autoclosure,
and @Sendable, and may be marked async and/or
throws (including typed throws):
typealias Loader = () async throws(NetworkError) -> Data
Protocol composition and boxed protocol types
A protocol composition combines requirements with
&: Codable & Sendable. To use a protocol as a
type — an existential that can hold any conforming value at runtime — you write
a boxed protocol type with any:
any Shape, any Collection. The any
keyword is required for protocols with associated types and is recommended
everywhere for clarity.
let values: [any Equatable] = []
func log(_ x: any Error & Sendable) {}
Opaque types
An opaque type with some names a single concrete
type the implementation chooses, while hiding which one from callers. Unlike
any, it preserves type identity, so a function returning
some Collection always returns the same underlying type. In
parameter position, some is shorthand for a generic parameter.
func makeView() -> some View { Text("Hello") }
func total(_ xs: some Sequence<Int>) -> Int { xs.reduce(0, +) }
Tip: reach for some (opaque) when there is one
concrete return/parameter type to hide; reach for any (boxed) only
when you genuinely need to store mixed concrete types together.
Metatype types and Self
A metatype is the type of a type. For a type T,
its metatype is written T.Type; for a protocol P, the
metatype of its existential is (any P).Type. You obtain a metatype
value with .self and inspect a value's dynamic type with
type(of:).
let t: Int.Type = Int.self
print(type(of: 3.0)) // → Double
Self (capital S) inside a type or protocol refers to the conforming
or enclosing type, which is essential for protocol requirements that return the
implementing type.
Type inference
Where you omit a type annotation, Swift infers it from context — from a literal, from the right-hand side of an assignment, or by unifying constraints across an expression. Inference is local to a statement; it does not span function boundaries, which is why function signatures must be fully typed.
Summary
Swift's type grammar layers a handful of compound forms (tuple, function, array,
dictionary, optional) on top of named type identifiers, then adds protocol
composition, the any/some existential and opaque forms,
metatypes, and Self. Type inference lets you write most types
implicitly while keeping the full grammar available when you need precision.