import { expected, failure, Failure, FullError, typesAreNotCompatible, unableToAssign, } from '../result'; import { Static, create, RuntypeBase, Codec, createValidationPlaceholder, assertRuntype, } from '../runtype'; import show from '../show'; export interface ReadonlyArray = RuntypeBase> extends Codec[]> { readonly tag: 'array'; readonly element: E; readonly isReadonly: true; } export { Arr as Array }; interface Arr = RuntypeBase> extends Codec[]> { readonly tag: 'array'; readonly element: E; readonly isReadonly: false; asReadonly(): ReadonlyArray; } /** * Construct an array runtype from a runtype for its elements. */ function InternalArr, IsReadonly extends boolean>( element: TElement, isReadonly: IsReadonly, ): IsReadonly extends true ? ReadonlyArray : Arr { assertRuntype(element); const result = create | Arr>( 'array', (xs, innerValidate, _innerValidateToPlaceholder, _getFields, sealed) => { if (!Array.isArray(xs)) { return expected('an Array', xs); } return createValidationPlaceholder([...xs], placeholder => { let fullError: FullError | undefined = undefined; let firstError: Failure | undefined; for (let i = 0; i < xs.length; i++) { const validated = innerValidate( element, xs[i], sealed && sealed.deep ? { deep: true } : false, ); if (!validated.success) { if (!fullError) { fullError = unableToAssign(xs, result); } fullError.push(typesAreNotCompatible(`[${i}]`, validated)); firstError = firstError || failure(validated.message, { key: validated.key ? `[${i}].${validated.key}` : `[${i}]`, fullError: fullError, }); } else { placeholder[i] = validated.value; } } return firstError; }); }, { isReadonly, element, show() { return `${isReadonly ? 'readonly ' : ''}${show(element, true)}[]`; }, }, ); if (!isReadonly) { (result as any).asReadonly = () => InternalArr(element, true); } return result as any; } function Arr>(element: TElement): Arr { return InternalArr(element, false); } export function ReadonlyArray>( element: TElement, ): ReadonlyArray { return InternalArr(element, true); }