Swift’s typealias explained with five examples
Published on: January 2, 2020Swift grants developers the ability to shadow certain types with an alternative name using the typealias
keyword. We can use this feature to create tuples and closures that look like types, or we can use them to provide alternative names for existing objects. In this post, we'll look at five ways in which typealiases can help you write cleaner and better code.
1. Improving readability with type aliases
Perhaps this is the most obvious yet also somewhat underused way to use a typealias
. When you have a type in your code that is very long or deeply nested, you could end up with rather long type declarations.
The following code snippet is an example of a rather long type name:
UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>
I have made the previous declaration nice and long on purpose, but it's not far from code that I have actually written. You can make the type declaration above much shorter with a simple typealias:
typealias CardDataSource = UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>
After defining this in your code, the Swift compiler will now know that whenever you write CardDataSource
, you really mean UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>
instead. This is really neat because instead of writing the same long, hard to read type declaration over and over, you can instead use the shorter, more descriptive type alias you created.
The same idea can be applied to functions that take a completion closure:
func fetchCards(_ completion: @escaping (Result<[CardSection], Error>) -> Void) {
// implementation
}
While the code above isn't horrible, we can make it a bit more readable with a typealias
:
typealias FetchCardsHandler = (Result<[CardSection], Error>) -> Void
func fetchCards(_ completion: @escaping FetchCardsHandler) {
// implementation
}
Reading this alternative function that uses a typealias
is easier on the eyes since there is much less information to unpack in the function declaration. On the other hand, if someone is trying to figure out what the FetchCardsHandler
is they will need to dig a little bit deeper than they would if the function directly exposed the closure's type signature. I'll leave it up to you to decide whether you prefer typealias or closure signatures in your functions since you could argue for either and make a compelling case in my opinion.
As you've seen in this first type, typealias
can improve readability but in some cases they can hurt discoverability. In the case of the diffable data source typealias
, this isn't much of a problem. In the closure case, this can be a bit more problematic. As with any technique, always consider your options carefully and when in doubt favor clarity and obviousness.
2. Communicate intention
A well-placed type alias can help developers determine what valid inputs for a certain method are, or what the inputs are used for. A good example is the TimeInterval
alias that's used on iOS. Whenever you see that a certain method or property has the type TimeInterval
you immediately get a sense of what kind of inputs you can use, and what will be done with them. Internally, however, a TimeInterval
is nothing more than a typealias
.
Another example of a type alias that communicates an intention to the user is a special ID
type that's used inside of a struct:
typealias ID = Int
struct Card {
let id: ID
// more properties and code
}
struct CardSection {
let id: ID
// more properties and code
}
In the preceding snippet, you can see that the Card
and CardSection
objects both have an id
property who's type is ID
. While this doesn't look that impressive, it's pretty neat. We can now enforce that Card
and CardSection
use the same type as their ID
by ensuring that they both use the same aliased type. In this case, both identifiers have to be integers. This can provide a lot of meaning to other developers reading this code and when used carefully can really make a big difference in your code.
3. Combining protocols
This is possibly one of the more interesting and obscure ways to use a typealias
that I actually use regularly myself. I even wrote an article about it a while ago so I won't go in-depth too much. In Swift, you can use a typealias to create a placeholder for something that conforms to one or more protocols. For example, you can do the following:
typealias CommonDataSource = UICollectionViewDataSource & UITableViewDataSource
The result of the code above is that any object that claims to be a CommonDataSource
must conform to both UICollectionViewDataSource
and UITableViewDataSource
. Since this example is cool but not very useful, let me give you another brief example:
public typealias Codable = Decodable & Encodable
Does the above look familiar? The idea of it probably does! If you declare a type to be Codable
, it's both Encodable
and Decodable
. The way this is defined in Swift is through a typealias
that looks exactly like the one in this code snippet.
4. Named tuples
In Swift, we can define something called a tuple. A tuple is a set of values that are grouped together. For example, take a look at the return type of the function below:
func divide(_ lhs: Int, _ rhs: Int) -> (result: Int, remainder: Int) {
return (lhs/rhs, lhs % rhs)
}
The code above returns a pair of values from a function called divide
. Instead of defining a struct or class to hold the two values that we'd like to return we can return a tuple of values which can be quite convenient for cases like this.
Since a tuple's definition on its own doesn't always tell us much about what the tuple represents, we can use a typealias
to give a name to this tuple to make its intent clear:
typealias DivisionResultAndRemainder = (result: Int, remainder: Int)
func divide(_ lhs: Int, _ rhs: Int) -> DivisionResultAndRemainder {
return (lhs/rhs, lhs % rhs)
}
Users of this code now understand what the returned tuple is, and how they can use it.
An interesting side effect of using tuples and type aliases like this is that you could even swap out the DivisionResultAndRemainder
tuple for a struct with the same properties without making any changes to callers of divide(_:_:)
which is pretty neat.
5. Defining a protocol's associated type
The fifth and last tip is a more advanced one that is used a lot in the Swift source code. Sometimes a protocol has an associated type:
protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
// more code...
}
Note:
If you're not familiar with associated types or if you want to learn more, I recommend that you read the following posts on this topic:
The Swift compiler is usually pretty good at inferring what the concrete types of these associated types should be. However, this can be rather confusing, especially if the associated type is used in more than one place. The most common use of type aliases in the Swift standard library is (by far) the explicit definition of associated types:
extension ClosedRange: Sequence where Bound: Strideable, Bound.Stride: SignedInteger {
public typealias Element = Bound
public typealias Iterator = IndexingIterator<ClosedRange<Bound>>
}
The code above is directly from the Swift standard library. It shows how ClosedRange
conforms to Sequence
under certain conditions, and in the extension body, the Element
and Iterator
associated types from Sequence
are defined. What's really interesting is that all Sequence
functionality can now be assigned to ClosedRange
through extensions on Sequence
rather than explicitly defining them on ClosedRange
.
This a great example of how protocol-oriented programming works in Swift, and it's made possible by typealias
! Pretty cool, right?
In Summary
In today's quick tip you learned about five different use cases for the typealias
keyword in Swift. Some of these use cases can significantly improve your code's readability while others have a more minor impact. You even learned that typealias
is used to power large parts of the Swift standard library and that without typealias
, protocol-oriented programming probably wouldn't look like it does today.
If you have more cool uses of typealias
to improve your code, if you have questions or feedback, don't hesitate to send me a Tweet.