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:

TypeSyntaxSugar for
Tuple(Int, String), (x: Int, y: Int)
Function(Int) -> Bool
Array[Element]Array<Element>
Dictionary[Key: Value]Dictionary<Key, Value>
OptionalWrapped?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.