all! I want to define a generic result data type, which is a union of a generic success type and a generic failure type. The same thing in TS looks like this:
type Success<T> = {
value: T
}
type Failure<E> = {
error: E
}
type Result<T, E> = Success<T> | Failure<E>
or in Rust like this:
enum Result<T, E> {
Ok(T),
Err(E),
}
But I, unfortunately, couldn't find a way to do that using Sorbet type annotations. Is that even possible?
Thank you very much.
The closest thing I've found was type definitions for the gem dry-monads
but it's not really what I want because it looks like a hack because both Success
and Failure
classes should redefine both type_members
.
Explanation
If you take a look at this example: https://gist.github.com/woarewe/f4f3ee502f35c4c0d097695a52031b14 My goal is to define a signature that looks like this:
sig { params(value: Integer).returns(Core::Type::Result[Integer, String]) }
But it seems that it's not possible cause it seems that there is no way to pass a generic type from one class to another.
The only workaround I found is to build a union with specific types right in the function definition:
sig { params(value: Integer).returns(T.any(Core::Type::Success[Integer], Core::Type::Failure[String])) }
def zero?(value)
if value.zero?
Core::Type::Success.new(value)
else
Core::Type::Failure.new("It is not zero")
end
end
Final solution looks like this
# typed: strict
# frozen_string_literal: true
module Core
module Type
class Success
extend T::Sig
extend T::Generic
ValueType = type_member
sig { returns(ValueType) }
attr_reader :value
sig { params(value: ValueType).void }
def initialize(value)
@value = value
end
end
class Failure
extend T::Sig
extend T::Generic
ErrorType = type_member
sig { returns(ErrorType) }
attr_reader :error
sig { params(error: ErrorType).void }
def initialize(error)
@error = error
end
end
end
end
extend T::Sig
sig { params(value: Integer).returns(T.any(Core::Type::Success[Integer], Core::Type::Failure[String])) }
def zero?(value)
if value.zero?
Core::Type::Success.new(value)
else
Core::Type::Failure.new("It is not zero")
end
end
result = zero?(0)
case result
when Core::Type::Success
p result.value
when Core::Type::Failure
p result.error
end