Skip to content

feature request - add uniqueItems and sorted properties to array type #72

@MarcelOldenkamp

Description

@MarcelOldenkamp

Similar to the existing properties minLength and minLength add support for properties uniqueItems and sorted.

  • Sorted should accept a selector function to specify the value to sort on (perhaps also sort order).
  • uniqueItems boolean true (or perhaps an optional selector function to only enforce uniqueness of a specific value)?

With this feature we would no longer need to define custom types like:

/**
 * An array with length > 0 and distinct values.
 * Allows custom comparator function to specify 'distinct' values, defaults to 'isEqual', which performs a deep comparison between two values for equality comparison.
 * Note that although array length is validated, the array element(s) itself may still be falsy (undefined etc.) depending on the elementType.
 * WARNING: Be sure to document the distinct behavior in de OpenApiMetadata of the type you are using this with so API consumers are aware
 * of this restriction
 * @param elementType the type of the array elements.
 */
type NonEmptyDistinctArray<T> = Branded<T[], 'NonEmptyDistinctArray'>;
function NonEmptyDistinctArray<T>(elementType: Type<T>, comparatorFn: Comparator<T> = isEqual): Type<NonEmptyDistinctArray<T>> {
    const type = NonEmptyArray<T>(elementType)
        // Set the brand to plain `NonEmptyDistinctArray`:
        .withConstraint('NonEmptyDistinctArray', value => {
            return uniqWith(value, comparatorFn).length === value.length || 'no duplicate values allowed';
        });
    // But set the runtime name to be more specific:
    Object.defineProperty(type, 'name', { value: `NonEmptyDistinctArray<${elementType.name}>` });
    return type;
}

type NonEmptyDistinctSortedDates<T> = Branded<T[], 'NonEmptyDistinctSortedDates' | 'NonEmptyDistinctArray'>;
function NonEmptyDistinctSortedDates<T extends ISODate>(elementType: Type<T>) {
    const type = NonEmptyDistinctArray<T>(elementType)
        // Set the brand to plain `NonEmptyDistinctSortedDates`:
        .withConstraint('NonEmptyDistinctSortedDates', value => {
            return value.every(pairwise((next, prev) => !prev || next > prev)) || 'should be sorted by date';
        });
    // But set the runtime name to be more specific:
    Object.defineProperty(type, 'name', { value: `SortedByDate<${elementType.name}>` });
    return type;
}

with:

/**
 * An array with length > 0.
 * Note that although array length is validated, the array element(s) itself may still be falsy (undefined etc.) depending on the elementType.
 * @param elementType the type of the array elements.
 */
export function NonEmptyArray<T>(elementType: Type<T>): Type<T[]> {
    return array(elementType).withConfig(`NonEmptyArray<${elementType.name}>`, {
        minLength: 1,
        customMessage: `expected at least one ${elementType.name}`,
    });
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions