What is TypeAlias?

Typealias is rarely the first thing that comes to mind when recalling Swift’s powerful language features. However, there are many situations where type aliases can be useful. This article gives you a brief overview of what TypeAliases are, how to define them, and lists several examples of how to use them in your own code. Let’s dive in!

As the name implies, TypeAlias is an alias for a specific type. Type, such as Int, Double, UIViewController, or a custom type. Int32 and Int8 are different types. In other words, a type alias inserts another name for an existing type into your code base. Such as:

typealias Money = Int
Copy the code

Create an alias for Int. This allows you to use Money anywhere in your code, just like Int:

struct Bank {  typealias Money = Int  private var credit: Money = 0  mutating func deposit(amount: Money) {    credit += amount  }  mutating func withdraw(amount: Money) {    credit -= amount  }}
Copy the code

There’s a structure on it called the Bank that manages the money. However, instead of using Int as the amount, we use the Money type. You can see that the += and -= operators still work as expected.

You can also mix type aliases and primitive types, and match both. We can do this because, to the Swift compiler, they all resolve to the same thing:

struct Bank {  typealias DepositMoney = Int  typealias WithdrawMoney = Int  private var credit: Int = 0  mutating func deposit(amount: DepositMoney) {    credit += amount  }  mutating func withdraw(amount: WithdrawMoney) {    credit -= amount  }}
Copy the code

Here we use a mixture of Int and its different custom type aliases, DepositMoney and 款 Money.

Generic type alias

In addition to the above, a type alias can also have generic parameters:

typealias MyArray<T> = Array<T>let newArray: MyArray = MyArray(arrayLiteral: 1, 2, 3)
Copy the code

Above, a type alias is defined for MyArray, just like a regular array. Finally, the generic parameters of a type alias can even have constraints. Imagine that we want our new MyArray to keep only types that follow StringProtocol:

typealias MyArray<T> = Array<T> where T: StringProtocol
Copy the code

This is a nice feature because you can quickly define an Array for a particular type without having to subclass Array. With that said, let’s look at some practical uses of type aliasing.

IOS Development Communication Technology Group: [563513413](Jq.qq.com/? \_wv= 1027&… , share BAT, Ali interview questions, interview experience, discuss technology, we exchange learning and growth together!

Practical application

Clearer code

The first, and obvious, use case has been briefly covered. Type aliases can make code more meaningful. In the TypeAlias Money = Int example, we introduce the Money type — a clear concept. Using it like let amount: Money = 0 is easier to understand than let amount: Int = 0. In the first example, you immediately know that this is

money

the

The amount of

. In the second example, it could be anything: the number of bikes, the number of characters, the number of donuts — who knows!

This is obviously not all necessary. If the function signature already clearly states the type of the parameter (func orderDonuts(amount: Int)), then including additional type identifiers would be unnecessary overhead. For variables and constants, on the other hand, it can generally improve readability and greatly aid in documentation.

Simpler optional closures

Optional closures in Swift are a bit clunky. The general definition of a closure that takes an Int and returns an Int looks like this:

func handle(action: (Int) -> Int) { ... }
Copy the code

Now, if you want to make this closure selectable, you can’t just add a question mark:

func handle(action: (Int) -> Int?) {... }Copy the code

After all, this is not a selectable closure, but rather

One returns an optional Int

The closures. The correct way is to add parentheses:

func handle(action: ((Int) -> Int)?) {... }Copy the code

This becomes particularly ugly if you have multiple closures like this. Below, there is a function that handles success and failure cases and calls an additional closure as the operation progresses.

func handle(success: ((Int) -> Int)? , failure: ((Error) -> Void)? , progress: ((Double) -> Void)?) {}Copy the code

This little piece of code contains

A lot of

Parentheses. Since we are not going to be Lisper, we want to solve this problem by using type aliases for different closures:

typealias Success = (Int) -> Inttypealias Failure = (Error) -> Voidtypealias Progress = (Double) -> Voidfunc handle2(success: Success? , failure: Failure? , progress: Progress?) {... }Copy the code

In fact, this function does look more readable. While this is nice, we do introduce additional syntax by using three lines of TypeAlias. But, it might actually help us in the long run, as we’ll see next.

defined

These specific types can be used for more than just the action handlers in the previous example. Here is an action handler class that has been slightly modified to be more practical:

final class Dispatcher { private var successHandler: ((Int) -> Void)? private var errorHandler: ((Error) -> Void)? func handle(success: ((Int) -> Void)? , error: ((Error) -> Void)?) { self.successHandler = success self.errorHandler = error internalHandle() } func handle(success: ((Int) -> Void)?) { self.successHandler = success internalHandle() } func handle(error: ((Int)-> Void?) ) { self.errorHandler = error internalHandle() } private func internalHandle() { ... }}Copy the code

This structure introduces two closures, one for the success case and one for the error case. However, we also want to provide more convenient functions that simply call one of the processors. In the example above, adding another parameter (such as HTTPResponse) to the success and error handlers would have required a lot of code change. In three places ((Int) -> Void)? ((Int, HTTPResponse) -> Void)? . The error handler is the same. This can be avoided by using multiple type aliases and changing the type in only one place:

final class Dispatcher { typealias Success = (Int, HTTPResponse) -> Void typealias Failure = (Error, HTTPResponse) -> Void private var successHandler: Success? private var errorHandler: Failure? func handle(success: Success? , error: Failure?) { self.successHandler = success self.errorHandler = error internalHandle() } func handle(success: Success?) { self.successHandler = success internalHandle() } func handle(error: Failure?) { self.errorHandler = error internalHandle() } private func internalHandle() { ... }}Copy the code

Not only is this easy to read, but it will continue to serve its purpose as the type is used in more places.

Generic alias

Type aliases can also be generic. A simple use case is to enforce containers with special meaning. Suppose we have an application that processes books. A book is made up of chapters and chapters are made up of pages. Basically, these are just arrays. Here is the TypeAlias:

struct Page {}typealias Chapter = Array<Page>typealias Book = Array<Chapter>
Copy the code

This has two advantages over just using arrays.

  1. This code is more explanatory.

  2. Wrap an array of pages

    only

    Can contain pages and not others.

Review our previous use

successful

and

failure

An example of a handler that we can further improve by using a generic handler:

typealias Handler<In> = (In, HTTPResponse? , Context) -> Voidfunc handle(success: Handler<Int>? , failure: Handler<Error>? , progress: Handler<Double>? .)Copy the code

It’s a great combination. This allows us to write a simpler function and edit the Handler in one place.

This approach is also useful for custom types. You can create a generic definition and then define the detailed type aliases:

struct ComputationResult<T> {  private var result: T}typealias DataResult = ComputationResult<Data>typealias StringResult = ComputationResult<String>typealias IntResult = ComputationResult<Int>
Copy the code

Again, type aliases allow us to write less code and simplify definitions in code.

A function like tuple

Similarly, you can use generics and tuples to define types, rather than having to use structs. Below, we imagine a data type of genetic algorithm that can modify its value T over multiple generations.

typealias Generation<T: Numeric> = (initial: T, seed: T, count: Int, current: T)
Copy the code

If you define such a type alias, you can actually initialize it as if it were a structure:

let firstGeneration = Generation(initial: 10, seed: 42, count: 0, current: 10)
Copy the code

Although it does look like a structure, it is just a type alias for a tuple.

Combination of agreement

Sometimes, you will run into situations where you have multiple protocols and need to use a particular type to implement them all. This usually happens when you define a protocol layer to improve flexibility.

protocol CanRead {}protocol CanWrite {}protocol CanAuthorize {}protocol CanCreateUser {}typealias Administrator = CanRead & CanWrite & CanAuthorize & CanCreateUsertypealias User = CanRead & CanWritetypealias Consumer = CanRead
Copy the code

Here, we define the permissions layer. Administrators can do everything, users can read and write, and consumers can only read.

Association types

This is beyond the scope of this article, but the associated type of a protocol can also be defined by a type alias:

protocol Example { associatedtype Payload: Numeric}struct Implementation: Example {  typealias Payload = Int}
Copy the code

disadvantages

Although type aliases are a very useful feature, they have a minor drawback: if you are not familiar with the code base, the following two definitions can be interpreted very differently.

func first(action: (Int, Error?) -> Void) {}func second(action: Success) {}
Copy the code

The second is not immediately clear. What type of Success is Success? How to construct it? You have to hold Option down and click on it in Xcode to see what it does and how it works. This creates extra work. If you use many type aliases, it will take more time. There is no good solution to this, and (usually) you have to rely on use cases.

Benedikt Terhechte, Author: Benedikt Terhechte Proofreading: WAMaker, Nemocdz; Finalized: Pancf