What is the Swift equivalent of a Typescript union type?

Viewed 2482

I want to create something like this on Swift It's for Alamofire json parse

interface Question {
  value: string;
  data: [string]
}

interface Advice {
  type: string;
  data: { value: string }
}

interface CurrentItem {
  type: string;
  data: Advice | Question | string
}
2 Answers

Use an enum with associated values to represent the different possibilities:

enum Data {
  case advice (Advice)
  case question (Question)
  case string (String)
}

You can then create your CurrentItem as a Struct

struct CurrentItem {
  type: String
  data: Data
}

If you're decoding from JSON you'll need to conform the struct to Decodable and write a custom init(from decoder: Decoder) method that looks at the data provided and then create the appropriate instance of the Data enum.

I think in Swift the way you'd handle a union type like this is with protocols.

struct Test {
    var value: MyProtocol
}

(Note I put struct but it can be whatever type of object you need.)

Where Type1, Type2 and Type3 conform to some shared protocol. Either by construction, or they can be extended to adopt the protocol. This is advantageous because your object doesn't have to be limited to one of three types and handle each one separately, but rather it doesn't have to know about the inner workings of each of the types it has to support.

And where MyProtocol would look something like:

protocol MyProtocol {
    var myValue: Bar { get set }
    func myMethod(_ foo: Foo) -> SomeType
}

And say you wanted to extend Type1 to adopt MyProtocol:

extension Type1: MyProtocol {
    var myValue: Bar { foo }
    func myMethod(_ foo: Foo) -> SomeType { bar }
}

Alternatively, Swift has lots of built-in protocols which might already be shared in common between your three types.

Note you could declare a type that conforms to a union of multiple types with & but Swift doesn't have a corresponding idea for "this type OR that type".

Note also that if your protocol has Self or associated type requirements it cannot be used as a generic constraint as shown above.

Read more about protocols in the Swift Language Guide.

However, if your three types don't have anything in common, Swift also has the Any type and in your code you can test for each type as needed using conditional casting:

struct Test { 
    var value: Any
    func doSomething() {
        if let value = value as? Type1 {
            doTheThing()
        } // etc.
    }
}
Related