validateFieldSpecification(typeOptions) {
    if (has(typeOptions, "formats")) {
      if (
        !Array.isArray(typeOptions.formats) ||
        typeOptions.formats.length === 0
      ) {
        throw assertionError.get("invalid_type", "formats", "non-empty array");
      }

      const unrecognized = typeOptions.formats.filter((f) => !formatMap[f]);
      if (unrecognized.length > 0) {
        throw typeError.get("invalid_date_format", unrecognized.join(", "));
      }
    } else {
      typeOptions.formats = ["epoch_millis"];
    }

    if (has(typeOptions, "range")) {
      let min = null,
        max = null;

      if (!this.checkAllowedProperties(typeOptions.range, ["min", "max"])) {
        throw assertionError.get("unexpected_properties", "range", "min, max");
      }

      if (has(typeOptions.range, "min")) {
        min = convertRangeValue("min", typeOptions.range.min);
      }

      if (has(typeOptions.range, "max")) {
        max = convertRangeValue("max", typeOptions.range.max);
      }

      if (min && max && max.isBefore(min)) {
        throw assertionError.get("invalid_range", "range", "min", "max");
      }

      // We want to keep NOW as a special value, else we would keep the time
      // of the launch
      if (min && typeOptions.range.min !== "NOW") {
        typeOptions.range.min = min;
      }

      if (max && typeOptions.range.max !== "NOW") {
        typeOptions.range.max = max;
      }
    }

    return typeOptions;
  }
@param {TypeOptions} typeOptions
@returns {TypeOptions}
@throws {PreconditionError}