# frozen_string_literal: true
# typed: true
# This is where we define the shortcuts, so we can't use them here
# _____
# |_ _| _ _ __ ___ ___
# | || | | | '_ \ / _ \/ __|
# | || |_| | |_) | __/\__ \
# |_| \__, | .__/ \___||___/
# |___/|_|
#
# Docs at https://sorbet.org/docs/sigs
#
# Types that you can pass to `sig`:
#
# - a Ruby class
#
# - [<Type>, <Type>, ...] -- to specify a "tuple"; a fixed-size array with known types for each member
#
# - {key: <Type>, key2: <Type>, ...} -- to speicfy a "shape"; a fixed-size hash
# with known keys and type values
#
# - Any of the `T.foo` methods below
module T
# T.any(<Type>, <Type>, ...) -- matches any of the types listed
def self.any(type_a, type_b, *types)
type_a = T::Utils.coerce(type_a)
type_b = T::Utils.coerce(type_b)
types = types.map {|t| T::Utils.coerce(t)} if !types.empty?
T::Types::Union::Private::Pool.union_of_types(type_a, type_b, types)
end
# Shorthand for T.any(type, NilClass)
def self.nilable(type)
T::Types::Union::Private::Pool.union_of_types(T::Utils.coerce(type), T::Utils::Nilable::NIL_TYPE)
end
# Matches any object. In the static checker, T.untyped allows any
# method calls or operations.
def self.untyped
T::Types::Untyped::Private::INSTANCE
end
# Indicates a function never returns (e.g. "Kernel#raise")
def self.noreturn
T::Types::NoReturn::Private::INSTANCE
end
# T.all(<Type>, <Type>, ...) -- matches an object that has all of the types listed
def self.all(type_a, type_b, *types)
T::Types::Intersection.new([type_a, type_b] + types)
end
# Matches any of the listed values
# @deprecated Use T::Enum instead.
def self.deprecated_enum(values)
T::Types::Enum.new(values)
end
# Creates a proc type
def self.proc
T::Private::Methods.start_proc
end
# Matches `self`:
def self.self_type
T::Types::SelfType::Private::INSTANCE
end
# Matches the instance type in a singleton-class context
def self.attached_class
T::Types::AttachedClassType::Private::INSTANCE
end
# Matches any class that subclasses or includes the provided class
# or module
def self.class_of(klass)
T::Types::ClassOf.new(klass)
end
## END OF THE METHODS TO PASS TO `sig`.
# Constructs a type alias. Used to create a short name for a larger type. In Ruby this returns a
# wrapper that contains a proc that is evaluated to get the underlying type. This syntax however
# is needed for support by the static checker.
#
# @example
# NilableString = T.type_alias {T.nilable(String)}
#
# sig {params(arg: NilableString, default: String).returns(String)}
# def or_else(arg, default)
# arg || default
# end
#
# The name of the type alias is not preserved; Error messages will
# be printed with reference to the underlying type.
#
# TODO Remove `type` parameter. This was left in to make life easier while migrating.
def self.type_alias(type=nil, &blk)
if blk
T::Private::Types::TypeAlias.new(blk)
else
T::Utils.coerce(type)
end
end
# References a type parameter which was previously defined with
# `type_parameters`.
#
# This is used for generic methods.
#
# @example
# sig
# .type_parameters(:U)
# .params(
# blk: T.proc.params(arg0: Elem).returns(T.type_parameter(:U)),
# )
# .returns(T::Array[T.type_parameter(:U)])
# def map(&blk); end
def self.type_parameter(name)
T::Types::TypeParameter.new(name)
end
# Tells the typechecker that `value` is of type `type`. Use this to get additional checking after
# an expression that the typechecker is unable to analyze. If `checked` is true, raises an
# exception at runtime if the value doesn't match the type.
#
# Compared to `T.let`, `T.cast` is _trusted_ by static system.
def self.cast(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.cast")
end
# Tells the typechecker to declare a variable of type `type`. Use
# like:
#
# seconds = T.let(0.0, Float)
#
# Compared to `T.cast`, `T.let` is _checked_ by static system.
#
# If `checked` is true, raises an exception at runtime if the value
# doesn't match the type.
def self.let(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.let")
end
# Tells the type checker to treat `self` in the current block as `type`.
# Useful for blocks that are captured and executed later with instance_exec.
# Use like:
#
# seconds = lambda do
# T.bind(self, NewBinding)
# ...
# end
#
# `T.bind` behaves like `T.cast` in that it is assumed to be true statically.
#
# If `checked` is true, raises an exception at runtime if the value
# doesn't match the type (this is the default).
def self.bind(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.bind")
end
# Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
# fail). Use this for debugging typechecking errors, or to ensure that type information is
# statically known and being checked appropriately. If `checked` is true, raises an exception at
# runtime if the value doesn't match the type.
def self.assert_type!(value, type, checked: true)
return value unless checked
Private::Casts.cast(value, type, cast_method: "T.assert_type!")
end
# For the static type checker, strips all type information from a value
# and returns the same value, but statically-typed as `T.untyped`.
# Can be used to tell the static checker to "trust you" by discarding type information
# you know to be incorrect. Use with care!
# (This has no effect at runtime.)
#
# We can't actually write this sig because we ourselves are inside
# the `T::` module and doing this would create a bootstrapping
# cycle. However, we also don't actually need to do so; An untyped
# identity method works just as well here.
#
# `sig {params(value: T.untyped).returns(T.untyped)}`
def self.unsafe(value)
value
end
# A convenience method to `raise` when the argument is `nil` and return it
# otherwise.
#
# Intended to be used as:
#
# needs_foo(T.must(maybe_gives_foo))
#
# Equivalent to:
#
# foo = maybe_gives_foo
# raise "nil" if foo.nil?
# needs_foo(foo)
#
# Intended to be used to promise sorbet that a given nilable value happens
# to contain a non-nil value at this point.
#
# `sig {params(arg: T.nilable(A)).returns(A)}`
def self.must(arg)
return arg if arg
return arg if arg == false
begin
raise TypeError.new("Passed `nil` into T.must")
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
T::Configuration.inline_type_error_handler(e, {kind: 'T.must', value: arg, type: nil})
end
end
# A way to ask Sorbet to show what type it thinks an expression has.
# This can be useful for debugging and checking assumptions.
# In the runtime, merely returns the value passed in.
def self.reveal_type(value)
value
end
# A way to ask Sorbet to prove that a certain branch of control flow never
# happens. Commonly used to assert that a case or if statement exhausts all
# possible cases.
def self.absurd(value)
msg = "Control flow reached T.absurd."
case value
when Kernel
msg += " Got value: #{value}"
end
begin
raise TypeError.new(msg)
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
T::Configuration.inline_type_error_handler(e, {kind: 'T.absurd', value: value, type: nil})
end
end
### Generic classes ###
module Array
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedArray::Untyped.new
else
T::Types::TypedArray.new(type)
end
end
end
module Hash
def self.[](keys, values)
if keys.is_a?(T::Types::Untyped) && values.is_a?(T::Types::Untyped)
T::Types::TypedHash::Untyped.new
else
T::Types::TypedHash.new(keys: keys, values: values)
end
end
end
module Enumerable
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedEnumerable::Untyped.new
else
T::Types::TypedEnumerable.new(type)
end
end
end
module Enumerator
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedEnumerator::Untyped.new
else
T::Types::TypedEnumerator.new(type)
end
end
module Lazy
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedEnumeratorLazy::Untyped.new
else
T::Types::TypedEnumeratorLazy.new(type)
end
end
end
end
module Range
def self.[](type)
T::Types::TypedRange.new(type)
end
end
module Set
def self.[](type)
if type.is_a?(T::Types::Untyped)
T::Types::TypedSet::Untyped.new
else
T::Types::TypedSet.new(type)
end
end
end
end