7.4 Type Information in Record Declarations

The types of record fields can be specified in the declaration of the record. The syntax for this is as follows:

  1. -record(rec, {field1 :: Type1, field2, field3 :: Type3}).

For fields without type annotations, their type defaults to any(). That is, the previous example is a shorthand for the following:

  1. -record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).

In the presence of initial values for fields, the type must be declared after the initialization, as follows:

  1. -record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3}).

The initial values for fields are to be compatible with (that is, a member of) the corresponding types. This is checked by the compiler and results in a compilation error if a violation is detected.


Before Erlang/OTP 19, for fields without initial values, the singleton type 'undefined' was added to all declared types. In other words, the following two record declarations had identical effects:

  1. -record(rec, {f1 = 42 :: integer(),
  2. f2 :: float(),
  3. f3 :: 'a' | 'b'}).
  5. -record(rec, {f1 = 42 :: integer(),
  6. f2 :: 'undefined' | float(),
  7. f3 :: 'undefined' | 'a' | 'b'}).

This is no longer the case. If you require 'undefined' in your record field type, you must explicitly add it to the typespec, as in the 2nd example.

Any record, containing type information or not, once defined, can be used as a type using the following syntax:

  1. #rec{}

In addition, the record fields can be further specified when using a record type by adding type information about the field as follows:

  1. #rec{some_field :: Type}

Any unspecified fields are assumed to have the type in the original record declaration.