I have a factory method, which receives a class and returns an instance. In Java, i can express it this way:
Java
class InstantiateMe { }
// Generic factory method
class Factory {
public static <V> V createInstance(Class<V> type) {
try {
return type.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// returns an instance of the class InstantiateMe
Factory.createInstance(InstantiateMe.class);
When trying to create a similar signature in sorbet, it ends up in an error. The error message of the static checker: T.class_of needs a Class as its argument
for the signature of Factory#createInstance
s.
ruby/sorbet:
# typed: true
class InstantiateMe; end
class Factory
extend T::Sig
sig do
type_parameters(:V)
params(klass: T.class_of(T.type_parameter(:V)))
.returns(T.type_parameter(:V))
end
def self.createInstance(klass)
klass.new
end
end
Factory.createInstance(Factory)
Check online: sorbet.run
Of course i see the point of accepting a class. However, the T.type_parameter(:V)
is actually expressing a variable class. So it should be accepted as well. The same way as it is accepted in Java.
However, maybe i am just expressing it wrong. What's the correct way to write a generic factory signature in sorbet?
The way to do this is somewhat hidden in the Sorbet documentation: https://sorbet.org/docs/class-of#tclass_of-applying-type-arguments-to-a-singleton-class-type
Short example:
class A; end
class Factory
extend T::Sig
sig do
type_parameters(:U)
.params(klass: T::Class[T.type_parameter(:U)])
.returns(T.type_parameter(:U))
end
def self.build(klass)
klass.new
end
end
a = Factory.build(A) # a is an instance of A
If you need to constrain the type parameter to children of a certain type, the type signature needs to be a little different:
class Base; end
class A < Base; end # can be passed to factory
class B; end # cannot be passed to factory, not a Base
class Factory
extend T::Sig
sig do
type_parameters(:U)
.params(klass: T.class_of(Base)[T.all(Base, T.type_parameter(:U))])
.returns(T.type_parameter(:U))
end
def self.build(klass)
klass.new
end
end
a = Factory.build(A)
b = Factory.build(B) # sorbet static type error