Skip to content

Latest commit

 

History

History
116 lines (90 loc) · 2.36 KB

type-aliases.md

File metadata and controls

116 lines (90 loc) · 2.36 KB
id title
type-aliases
Type Aliases

TODO: This page is still a fragment. Contributions welcome!

Alias = T.type_alias {Type}

This creates a type alias of Type called Alias. In the context of Sorbet, the type alias has exactly the same behavior as the original type and can be used anywhere the original type can be used. The converse is also true.

Note that the type alias will not show up in error messages.

# typed: true
extend T::Sig

Int = T.type_alias {Integer}
Str = T.type_alias {String}

sig {params(x: Int).returns(Str)}
def foo(x)
  T.reveal_type(x) # Revealed type: Integer
  x.to_s
end

a = T.let(3, Integer)
foo(a)
b = T.let(3, Int)
foo(b)

c = foo(3)
T.reveal_type(c) # Revealed type: String

When creating a type alias from another type alias, you must use T.type_alias again:

A = T.type_alias {Integer}
B = T.type_alias {A}

For simple use cases, type aliases are nearly identical to just making a new constant:

# typed: true
extend T::Sig

A = T.type_alias {Integer}
sig {returns(A)}
def foo; 3; end

B = Integer
sig {returns(B)}
def bar; 3; end

However, when the type is more complex, you must use type aliases:

# typed: true
extend T::Sig

A = T.type_alias {T.any(Integer, String)}
sig {returns(A)}
def foo; 3; end

B = T.any(Integer, String)
sig {returns(B)} # error: Constant B is not a class or type alias
def bar; 3; end

Note that because type aliases are a Sorbet construct, they cannot be used in certain runtime contexts. For instance, it is not possible to match an expression against a type alias in a case expression.

# typed: true
extend T::Sig

class A; end
class B; end
class C; end

AB = T.type_alias {T.any(A, B)}
sig {params(x: T.any(AB, C)).returns(Integer)}
def invalid(x) # error: Returning value that does not conform to method result type
  case x
  when AB then 1 # <- this line is problematic
  when C then 2
  end
end

We could refactor this example to use A, B in the when and AB in the sig. However, this introduces coupling between the definition of AB and our method. If we ever updated the definition of AB, we would need to update the definition of our method as well.

sig {params(x: T.any(AB, C)).returns(Integer)}
def valid(x)
  case x
  when A, B then 1
  when C then 2
  end
end