docs: def validate_types(self, definition):
          """This function performs some basic sanity checks on the scalar definition:
              - Checks that all the required fields are available.
              - Checks that all the fields have the expected types.
  
          :param definition: the dictionary containing the scalar properties.
          :raises ParserError: if a scalar definition field is of the wrong type.
          :raises ParserError: if a required field is missing or unknown fields are present.
          """
  
          if not self._strict_type_checks:
              return
  
          # The required and optional fields in a scalar type definition.
          REQUIRED_FIELDS = {
              'bug_numbers': list,  # This contains ints. See LIST_FIELDS_CONTENT.
              'description': string_types,
              'expires': string_types,
              'kind': string_types,
              'notification_emails': list,  # This contains strings. See LIST_FIELDS_CONTENT.
              'record_in_processes': list,
          }
  
          OPTIONAL_FIELDS = {
              'cpp_guard': string_types,
              'release_channel_collection': string_types,
              'keyed': bool,
          }
  
          # The types for the data within the fields that hold lists.
          LIST_FIELDS_CONTENT = {
              'bug_numbers': int,
              'notification_emails': string_types,
              'record_in_processes': string_types,
          }
  
          # Concatenate the required and optional field definitions.
          ALL_FIELDS = REQUIRED_FIELDS.copy()
          ALL_FIELDS.update(OPTIONAL_FIELDS)
  
          # Checks that all the required fields are available.
          missing_fields = [f for f in REQUIRED_FIELDS.keys() if f not in definition]
          if len(missing_fields) > 0:
              raise ParserError(self._name + ' - missing required fields: ' +
                                ', '.join(missing_fields) +
                                '.\nSee: {}#required-fields'.format(BASE_DOC_URL))
  
          # Do we have any unknown field?
          unknown_fields = [f for f in definition.keys() if f not in ALL_FIELDS]
          if len(unknown_fields) > 0:
              raise ParserError(self._name + ' - unknown fields: ' + ', '.join(unknown_fields) +
                                '.\nSee: {}#required-fields'.format(BASE_DOC_URL))
  
          # Checks the type for all the fields.
          wrong_type_names = ['{} must be {}'.format(f, utils.nice_type_name(ALL_FIELDS[f]))
                              for f in definition.keys()
                              if not isinstance(definition[f], ALL_FIELDS[f])]
          if len(wrong_type_names) > 0:
              raise ParserError(self._name + ' - ' + ', '.join(wrong_type_names) +
                                '.\nSee: {}#required-fields'.format(BASE_DOC_URL))
  
          # Check that the lists are not empty and that data in the lists
          # have the correct types.
          list_fields = [f for f in definition if isinstance(definition[f], list)]
          for field in list_fields:
              # Check for empty lists.
              if len(definition[field]) == 0:
                  raise ParserError(("Field '{}' for probe '{}' must not be empty" +
                                     ".\nSee: {}#required-fields)")
                                    .format(field, self._name, BASE_DOC_URL))
              # Check the type of the list content.
              broken_types =\
                  [not isinstance(v, LIST_FIELDS_CONTENT[field]) for v in definition[field]]
              if any(broken_types):
                  raise ParserError(("Field '{}' for probe '{}' must only contain values of type {}"
                                     ".\nSee: {}#the-yaml-definition-file)")
                                    .format(field, self._name, utils.nice_type_name(LIST_FIELDS_CONTENT[field]),
                                            BASE_DOC_URL))