Defining New Types

The attribute @type can be used to define new types.

 @type type_name :: type_specification

Elixir uses this to predefine some built-in types and aliases. Here are just some of them.

 @type term :: any
 @type binary :: <<_::_*8>>
 @type bitstring :: <<_::_*1>>
 @type boolean :: false | true
 @type byte :: 0..255
 @type char :: 0..0x10ffff
 @type charlist :: [ char ]
 @type list :: [ any ]
 @type list(t) :: [ t ]
 @type number :: integer | float
 @type module :: atom
 @type mfa :: {module, atom, byte}
 @type node :: atom
 @type nonempty_charlist :: [ char ]
 @type timeout :: ​:infinity​ | non_neg_integer
 @type no_return :: none

As the list(t) entry shows, you can parameterize the types in a new definition. Simply use one or more identifiers as parameters on the left side, and use these identifiers where you’d otherwise use type names on the right. Then when you use the newly defined type, pass in actual types for each of these parameters:

 @type variant(type_name, type) :: { ​:variant​, type_name, type)
 
 @spec create_string_tuple(​:string​, String.t) :: variant(​:string​, String.t)

As well as @type, Elixir has the @typep and @opaque module attributes. They have the same syntax as @type, and do basically the same thing. The difference is in the visibility of the result.

@typep defines a type that is local to the module that contains it—the type is private. @opaque defines a type whose name may be known outside the module but whose definition is not.