Erhannis
Erhannis

Reputation: 4462

Swift generic T.Type becomes T.Protocol

Swift 5.1

I'm writing a class that has a generic parameter T, and one of its methods accepts a type as an argument, where the type extends from T.Type. See foo1 below:

public protocol P {
}

public class C: P {
}

public class X<T> {
    public func foo1(_ t: T.Type) { // The function in question
    }

    public func foo2(_ t: P.Type) {  // Note that this works as expected, but is not generic
    }
}

public func test() {
    let x = X<P>()
    x.foo1(C.self) // ERROR Cannot convert value of type 'C.Type' to expected argument type 'P.Protocol'
    x.foo2(C.self) // Works as expected, but is not generic
}

Now, foo1 works fine when T is a class. However, when T is a protocol (e.g. P), Swift seems to rewrite my function signature from T.Type to T.Protocol.

  1. Why did it do this?
  2. How do I instead get foo1 to accept a type that inherits from P?

Class X is used in a number of other places - any changes to it must not restrict or remove class parameter T nor reduce X's functionality, nor make explicit reference to C or P. (It would be acceptable to constrain T to exclude protocols that do not extend AnyObject; I don't know how to do that, though. It might also be acceptable to e.g. create a subclass of X that adds the ability to handle a protocol in T, but I'm not sure how to do that, either.)

For clarity, this class is used to register classes (t) that conform to some specified parent (T), for more complicated project reasons. (Note that classes are being registered, not instances thereof.) The parent is given at the creation of X, via the type parameter. It works fine for a T of any class, but I'd also like it to work for a T of any protocol - or at least for a T of any protocol P: AnyObject, under which circumstances foo1 should accept any subclass of P...the same way it works when T is a class.

Upvotes: 0

Views: 478

Answers (1)

Asperi
Asperi

Reputation: 257563

Even though C is a P, but C.Type != P.Type, so the error.

But generics works in a bit different way, like in below examples:

public class X {
    public func foo1<T>(_ t: T.Type) { // this way
    }

    public func foo3<T:P>(_ t: T.Type) { // or this way
    }

    public func foo2(_ t: P.Type) {  // Note that this works as expected, but is not generic
    }
}

public func test() {
    let x = X()
    x.foo1(C.self) // << works
    x.foo3(C.self) // << works
    x.foo2(C.self) // Works as expected, but is not generic
}

Upvotes: 1

Related Questions