Type Placeholders in Swift: A New Level of Type Inference

KD Knowledge Diet
4 min readApr 20, 2024

Swift, a powerful and expressive programming language, has always been known for its robust type inference system. It minimizes the need for developers to explicitly specify types, making code concise and readable. With each new Swift version, the language continues to evolve, introducing features that enhance type inference. In this blog post, we’ll explore one such feature introduced in Swift 5.6: type placeholders.

The Power of Type Inference in Swift

Before delving into type placeholders, let’s appreciate Swift’s existing type inference capabilities. Consider the following line of code:

var number = 7

In this simple assignment, Swift infers that the value `7` is an `Int`, allowing you to declare the variable `number` without specifying its type explicitly. Swift’s type inference shines in such cases and greatly contributes to the language’s readability and conciseness.

Enter Swift 5.6: Introducing Type Placeholders

Swift 5.6, part of Xcode 13.3, builds upon its strong type inference foundation by introducing the concept of “type placeholders.” These placeholders come in handy when working with collections and other generic types, allowing you to write more concise and expressive code.

Using Type Placeholders with Generic Types

Let’s explore the practical use of type placeholders. Imagine you want to create an instance of Combine’s `CurrentValueSubject` with a default integer value. Initially, you might attempt to do this:

let counterSubject = CurrentValueSubject(0) // Error: "Generic parameter 'Failure' could not be inferred"

However, `CurrentValueSubject` is a generic type that requires specialization not only for its `Output` type but also for its `Failure` type, representing the type of errors it can throw. To indicate that this subject doesn’t throw errors (`Never`), you would need to specify both types:

let counterSubject = CurrentValueSubject<Int, Never>(0)

With Swift 5.6, you can use a type placeholder for the `Output` type, letting the compiler infer the type automatically:

let counterSubject = CurrentValueSubject<_, Never>(0)

Although the savings in characters may seem modest, this feature shines when dealing with more complex types.

Scaling Up: Complex Types Made Easy

Consider a scenario where you have a function that returns a complex generic type:

func loadAnnotatedPDF(named: String) -> Resource<PDF<UserAnnotations>> {
// Implementation…
}

The `Resource` type here is complex due to its generic nature and the use of phantom types. Using type placeholders, you can create an instance of `CurrentValueSubject` that perfectly matches the output of your function:

let pdfSubject = CurrentValueSubject<_, Never>(loadAnnotatedPDF(named: name))

This not only reduces the need for manual type annotations but also makes your code more maintainable. If you ever change the return type of the `loadAnnotatedPDF` function, there will be fewer annotations to update throughout your codebase.

Type Aliases vs. Type Placeholders

While type placeholders offer significant advantages, it’s essential to consider other ways to leverage Swift’s type inference capabilities. One alternative is using type aliases. For example, you could define a type alias to simplify the creation of subjects that don’t throw errors:

typealias UnfailingValueSubject<T> = CurrentValueSubject<T, Never>

With this alias, you can create subjects without any generic type annotations:

let pdfSubject = UnfailingValueSubject(loadAnnotatedPDF(named: name))

However, be cautious about overusing type aliases, as they can make your codebase more complicated. Sometimes, specifying types inline, as allowed by type placeholders, is the cleaner and more self-contained approach.

Type Placeholders in Collections

Type placeholders aren’t limited to generic types. You can also use them with collection literals, such as when creating dictionaries. In this example, we specify the dictionary’s `Key` type explicitly while using a type placeholder for the values:

enum UserRole {
case local
case remote
}
let latestMessages: [UserRole: _] = [
.local: "",
.remote: ""
]

This usage simplifies dictionary creation, especially when dealing with enums or other complex key types.

Wrapping Up

Type placeholders introduced in Swift 5.6 offer a powerful way to enhance type inference in your code, particularly when working with complex generic types. They simplify code, reduce the need for manual type annotations, and improve code maintainability.

While type aliases remain a valuable tool, type placeholders provide a lightweight alternative that allows Swift to automatically infer types. This feature demonstrates Swift’s commitment to making code cleaner and more expressive.

As you explore and incorporate type placeholders into your Swift projects, you’ll likely find them invaluable for creating more maintainable and concise code. Swift continues to evolve, making the language more powerful and developer-friendly with each release.

If you have questions or feedback about this feature or anything else related to Swift, please don’t hesitate to reach out via Twitter or email. Happy coding!

Thanks for reading!

--

--

KD Knowledge Diet

Software Engineer, Mobile Developer living in Seoul. I hate people using difficult words. Why not using simple words? Keep It Simple Stupid!