Releases: noshiro-pf/ts-data-forge
Release list
ts-data-forge@14.2.0
Minor Changes
-
695411a: Make the curried
Optional.map,Result.mapandResult.mapErrusable from
a caller that is itself generic over the container.Each curried overload used to fix its parameter to the container spelled in
terms of the mapper's input —(optional: Optional<S>) => …,
<E>(result: Result<S, E>) => …,<S>(result: Result<S, E>) => …. Because
S(resp.E) is bound when the mapper is passed, a caller generic over the
container ends up needingOto be assignable toOptional<Unwrap<O>>, which
TypeScript cannot show for a bareO extends UnknownOptional— even though
every concrete instantiation satisfies it. The curried form was therefore
unreachable from such a caller, which had to fall back to the direct, two-argument
Optional.map(a, mapFn).The returned function is now generic over the whole container, mirroring the
direct overload:export function map<S, S2>( mapFn: (value: S) => S2, ): <O extends UnknownOptional>( optional: Unwrap<O> extends S ? O : never, ) => Optional<S2>;
The conditional preserves the check — mapping a
(s: string) => …over an
Optional<number>is still an error — at the cost of a worse message for that
case (not assignable to 'never'rather than naming the two optionals).
Ordinary curried usage is unaffected and still infers the result precisely.
eslint-plugin-ts-data-forge@0.4.3
Patch Changes
- Updated dependencies [695411a]
- ts-data-forge@14.2.0
ts-data-forge@14.1.0
Minor Changes
-
f9f6b08:
Arr.everyused as a type guard now narrows to the element-substituted array
rather than to a barereadonly S[], so a tuple stays a tuple ofSand a
length-constrained array keeps its brand:declare const xs: MinLengthArray<3, string | number>; if (Arr.every(xs, isString)) { // now assignable to MinLengthArray<3, string>; previously it was not takesNonEmptyStrings(xs); } declare const pair: FixedLengthTuple<2, string | number>; if (Arr.every(pair, isString)) { // now assignable to readonly [string, string]; previously it was not takesStringPair(pair); }
The old predicate said
array is readonly S[]. TypeScript intersected that
with the declared type, so indexed access happened to come out right, but the
narrowed type was not assignable to the same container withSelements —
callers had to reach for a type assertion to pass it on.Brand-carrying arrays select a separate overload returning
ChangeArrayElement<Ar, S> & Ar; everything else states the homomorphic
mapping directly, for the same reason as inArr.map— a genericAr
cannot decideHasLengthConstraint. Both are intersected with the input, so a
brand intersected with an exact tuple — the shapeArr.isMinLengthArrayand
Arr.asMinLengthArrayproduce — keeps the brand, the length and the positions
all at once.The curried form gets the same treatment. Its two cases are overloads of the
returned guard rather than ofArr.everyitself, so a single
Arr.every(predicate)value still accepts branded and unbranded arrays alike.Arr.everywith a plainbooleanpredicate, andArr.some, are unchanged.
Patch Changes
-
20c6d36:
Arr.toSortedis usable from a function that is itself generic over the array.
Its parameter list was a conditional type — a tuple with an optional comparator
forreadonly number[]and a required one otherwise — and a genericArcannot
decide it, so the whole argument list was rejected:const sortAscending = <const T extends readonly number[]>( xs: T, ): readonly T[number][] => Arr.toSorted(xs, (a, b) => a - b); // Argument of type '[T, (a: number, b: number) => number]' is not assignable // to parameter of type 'T extends readonly number[] ? ... : ...'
The two cases are now two overloads, so resolution picks one per call. Concrete
callers are unaffected — the optional comparator for numbers, the required one
for everything else, and every reported result type are unchanged.This is the same class of problem 14.0.1 fixed for
Arr.map,Arr.toFilled
andArr.toRangeFilled, except on the parameter side rather than the return
side.Arr.tail,Arr.butLast,Arr.zip,Arr.setandArr.toUpdatedstill do not
resolve under a generic array parameter. They reportConstrainedList.Tail/
Zip/SetAt, whose brand branch computes on the input's own bounds, and that
computation expands the wholeSupportedLengthunion when the input is a type
parameter. Fixing those needs a change in ts-type-forge, not here; the reason is
recorded inarray-utils-shape-invariants.test.mtsalongside the coverage.
eslint-plugin-ts-data-forge@0.4.2
ts-data-forge@14.0.1
Patch Changes
-
c3cbdbd:
Arr.map,Arr.toFilledandArr.toRangeFilledagain report the plain
homomorphic mapping ({ [K in keyof Ar]: B }) for an array or tuple that
carries no length brand, so transforming a tuple into a same-length tuple
works inside a function that is itself generic over the tuple:const mapValues = <const T extends readonly Readonly<{ v: unknown }>[]>( boxes: T, ): Readonly<{ [K in keyof T]: unknown }> => Arr.map(boxes, (b) => b.v);
Since v14 the return type was a conditional branching on
HasLengthConstraint<Ar>, which a genericArcannot decide. The
conditional stayed deferred and its branded branch made the result
unassignable to the caller's own mapping, so callers had to reach for a type
assertion. Concrete tuples were unaffected and keep the same result as before.Brand-carrying arrays are unaffected too: they select a separate overload that
still returnsChangeArrayElement<Ar, …>, soArr.mapon a
MinLengthArray<2, number>still yields aMinLengthArray<2, …>.Arr.toSorted,Arr.toSortedBy,Arr.toReversed,Arr.toUpdated,
Arr.tail,Arr.butLastandArr.zipstill do not resolve under a generic
array parameter and are unchanged here.
eslint-plugin-ts-data-forge@0.4.1
Patch Changes
- Updated dependencies [c3cbdbd]
- ts-data-forge@14.0.1
eslint-plugin-ts-data-forge@0.4.0
Minor Changes
-
0dcdcbc:
prefer-is-record-and-has-keynow drops theisRecord(...)conjunct when the
object already satisfieshasKey'sR extends UnknownRecordconstraint, so
Object.hasOwn(record, key)on a record-typed value rewrites to
hasKey(record, key)instead ofisRecord(record) && hasKey(record, key).The check needs type information; without it the guard is kept, as before. It
is deliberately conservative — a type TypeScript would accept through an
implicit index signature keeps the guard — and callables and arrays keep it
too, becauseisRecordrejects those at runtime.no-unnecessary-type-guardrecognizesisRecordfor the same reason: it now
reportsisRecord(x)as alwaystruewhen every union member already
satisfiesUnknownRecord, and as alwaysfalsewhen none of them can (every
primitive, array, tuple and callable).
Patch Changes
- 938bc58: Widen the
@typescript-eslint/utilsdependency to^8.65.0and pin
ts-type-forgeto~9.1.1.
ts-data-forge@14.0.0
Major Changes
-
4dfe5d9: The
ArrandStrlength-constrained guards and casts now take their length
arguments first, matching the type-parameter order of the ts-type-forge types
they narrow to, and eachArrone gained a curried form.before after Arr.isMinLengthArray(xs, 3)Arr.isMinLengthArray(3, xs)Arr.isMaxLengthArray(xs, 8)Arr.isMaxLengthArray(8, xs)Arr.isFixedLengthArray(xs, 3)Arr.isFixedLengthArray(3, xs)Arr.isBoundedLengthArray(xs, 1, 5)Arr.isBoundedLengthArray(1, 5, xs)Arr.isFixedLengthTuple(xs, 2)Arr.isFixedLengthTuple(2, xs)Arr.isMinLengthTuple(xs, 1)Arr.isMinLengthTuple(1, xs)Arr.isMaxLengthTuple(xs, 2)Arr.isMaxLengthTuple(2, xs)Arr.isBoundedLengthTuple(xs, 1, 3)Arr.isBoundedLengthTuple(1, 3, xs)Arr.asMinLengthArray(xs, 3)Arr.asMinLengthArray(3, xs)Arr.asMaxLengthArray(xs, 8)Arr.asMaxLengthArray(8, xs)Arr.asFixedLengthArray(xs, 3)Arr.asFixedLengthArray(3, xs)Arr.asBoundedLengthArray(xs, 1, 5)Arr.asBoundedLengthArray(1, 5, xs)Strgets the same treatment, so the two namespaces read alike:before after Str.isMinLengthString(s, 12)Str.isMinLengthString(12, s)Str.isMaxLengthString(s, 32)Str.isMaxLengthString(32, s)Str.isFixedLengthString(s, 2)Str.isFixedLengthString(2, s)Str.isBoundedLengthString(s, 8, 16)Str.isBoundedLengthString(8, 16, s)Str.asMinLengthString(s, 12)Str.asMinLengthString(12, s)Str.asMaxLengthString(s, 32)Str.asMaxLengthString(32, s)Str.asFixedLengthString(s, 2)Str.asFixedLengthString(2, s)Str.asBoundedLengthString(s, 8, 16)Str.asBoundedLengthString(8, 16, s)The type parameters follow the same order — the container (
Xs/S/E)
now comes after the lengths, e.g.Arr.asMinLengthArray<Xs, MinLength>is now
Arr.asMinLengthArray<MinLength, Xs>— so an explicit type argument list reads
in the same order as the call.Omitting the array or string now returns a reusable guard/cast, so the curried
form is plain partial application of the same signature:const hasThree = Arr.isMinLengthArray(3); const isSmallSelection = Arr.isBoundedLengthArray(1, 5); const asRgb = Arr.asFixedLengthArray(3);
The eight
Arr.is*guards gained curried overloads; theas*casts in both
namespaces already had one, and its argument order is unchanged.BREAKING CHANGE: every call to the twenty functions above must swap its
arguments so the length comes first, and an explicit type argument list must
move the container type last. The curriedas*overloads are unaffected.Length-preserving array utilities now keep the branded length constraint of
their input.Arr.mapwas outright broken for a length-branded input:
MinLengthArray<3, E>is an intersection of a tuple and the brand object, which
TypeScript does not treat as an array type, so the homomorphic
Readonly<{ [K in keyof Ar]: B }>mappedlength, the array methods and the
brand keys toBas well and produced a non-array object. It now goes through
ts-type-forge'sChangeArrayElement, which rebuilds the structural part from the
bounds recovered from the brand and re-applies the brand:declare const history: MinLengthArray<3, number>; const labels = Arr.map(history, (n) => `#${n}`); // MinLengthArray<3, string> const first: string = labels[0]; // no `undefined`, as before the map
Arr.toSorted,Arr.toSortedBy,Arr.toReversed,Arr.set,Arr.toUpdated,
Arr.toFilledandArr.toRangeFilledpreviously degraded a branded input to
NonEmptyArray(or a plain array) and now keep the full constraint too.
(Arr.copyalready returned its input type, so it was never affected.)Arr.partition/Arr.chunknow report the bounds of each chunk, and
Arr.create/Arr.newArrayreport an exact length past the tuple range:call before after Arr.partition(xs, 2)readonly E[][]readonly BoundedLengthArray<1, 2, E>[]Arr.create(12, 'x')NonEmptyArray<'x'>FixedLengthArray<12, 'x'>The chunk's lower bound is the point:
partitionnever emits an empty chunk,
so indexed access into one is no longerundefinedat position0.const [firstChunk] = Arr.partition(values, 2); if (firstChunk !== undefined) { const first = firstChunk[0]; // not `| undefined` }
BREAKING CHANGE: a chunk is now branded, so an expected value compared against
one needs the unbranded type spelled out —assert.deepStrictEqual<readonly (readonly number[])[]>(chunks, […]).
Arr.zerosandArr.seqare deliberately unchanged: their argument type only
carries literals up toSmallUint, which they already expand into an exact
tuple, so there is no case left for a brand to describe.The array operations now report the right length constraint for a
length-constrained input, via ts-type-forge'sConstrainedList.Arr.set,Arr.toUpdated,Arr.tail,Arr.butLast,Arr.toReversedand
Arr.zipwere annotated withList.*, which decides what it can say from
whether the input is a fixed-length tuple. A length-constrained array is not one
— itslengthisnumber— soListhanded the input type straight back. For
the operations that shorten an array that is not merely imprecise, it is
wrong:declare const xs: MinLengthArray<5, string>; Arr.tail(xs); // before: MinLengthArray<5, string> — a 4-element result claiming at least five // after: MinLengthArray<4, string>
Arr.setandArr.toUpdatedgain positional precision from the same change:Arr.set([1, 2, 3] as const, 1, 'x'); // before: readonly [1 | 'x', 2 | 'x', 3 | 'x'] // after: readonly [1, 'x', 3]
Arr.toReversedalso drops a hand-rolled workaround
(HasLengthConstraint<Ar> extends true ? ChangeArrayElement<Ar, Ar[number]> : List.Reverse<Ar>)
which kept the length but collapsed the element positions to their union. One
annotation now covers plain tuples and branded arrays alike.A brand intersected with an exact tuple — the shape
Arr.isMinLengthArray(3, xs)
produces — keeps all three of the brand, the exact length and the positions.A union index is handled soundly, upstream.
ArgArrayIndex<Ar>for a tuple
is the union of its indices, so this is reachable from ordinary code:declare const i: 0 | 2; Arr.set([1, 2, 3] as const, i, 'x');
The call replaces one position or the other and never both, so a positional
answer ofreadonly ['x', 2, 'x']is a type that no possible result satisfies.
The cause was upstream:List.SetAtwalked the tuple askingPosition extends I,
which every member of a union answers at once, so all of them were replaced.
ts-type-forge 9.1.1 fixes that at the source, soArr.setandArr.toUpdated
pass the index straight through. Upstream widens only the positions the index can
actually name:Arr.set([1, 2, 3] as const, i, 'x'); // i: 0 | 2 // readonly [1 | 'x', 2, 3 | 'x'] — index 1 is not among `0 | 2`, so it stays `2`
A single literal index keeps the exact positional answer, as before.
Arr.take,Arr.takeLast,Arr.skipandArr.skipLastare sound now, but
not more precise. They used to hand a branded input straight back —
Arr.take(xs, 2)on an "at least 5" array claimed the two-element result still
held at least five. They now return the unconstrainedreadonly E[]for a
branded input, and keep the exactList.*tuple result for a plain array or
tuple.They do not yet use
ConstrainedList.Takeand friends. Against ts-type-forge
9.1.0 they could not: those rebuilt the bounds fromNand could not be
instantiated against a still-genericArat all. 9.1.1 lifts that — it no
longer distributes overN, so the instantiation stays bounded — which makes
propagating the bound through these four possible as a follow-up. It is left out
of this release because it changes their result type for branded inputs and
deserves its own measurement.Arr.toSortedandArr.toSortedBydeliberately keep their existing
annotation. An arbitrary permutation may move any element anywhere, so "same
length, element type widened to the union" stays the honest answer for them.Type-parameter order now follows value-parameter order everywhere. An audit
comparing each type parameter's declaration position against the first value
parameter that mentions it turned up two more signatures out of step, both
reordered:function before after Arr.sumBy(array, mapFn)<N, E><E, N>`Arr.partition(array, c...
ts-data-forge@13.0.0
Major Changes
-
534ac4d:
Arr.isEmptyandArr.isNonEmptynow narrow to the branded length-constrained
array types, matching the rest of theArrlength-guard family.guard before after Arr.isEmpty(xs)readonly []FixedLengthArray<0, E> & XsArr.isNonEmpty(xs)NonEmptyArray<E>MinLengthArray<1, E> & XsisEmptywas missing the brand entirely, so it was not equivalent to
Arr.isFixedLengthArray(xs, 0);isNonEmptyhad the brand but dropped the input
type instead of intersecting with it the wayArr.isMinLengthArraydoes. Both
now behave exactly like theiris*LengthArraycounterparts.BREAKING CHANGE: the narrowed types are strictly narrower than before. Code that
reads the narrowed value keeps compiling, but an explicit annotation such as
const empty: readonly [] = xsafter the guard, or passing the narrowed value
where an unbranded array literal is expected, may now need the unbranded type
spelled out.prefer-canonical-length-guardfollows the new semantics:isFixedLengthArray(xs, 0)
andisMinLengthArray(xs, 1)are now the type-identical rewrites, and the
structural*Tupleguards (isFixedLengthTuple(xs, 0),isMaxLengthTuple(xs, 0),
isBoundedLengthTuple(xs, 0, 0),isMinLengthTuple(xs, 1)) are rewritten too —
those strengthen the narrowed type by adding the brand.prefer-canonical-length-guardadditionally absorbs the fivexs.length <op> n
comparison rules —prefer-arr-is-non-empty,prefer-arr-is-min-length-array,
prefer-arr-is-max-length-array,prefer-arr-is-bounded-length-arrayand
prefer-arr-is-fixed-length-array— so one rule now covers both
comparison → guard and guard → guard normalization.BREAKING CHANGE: those five rule names are removed from the plugin; enable
ts-data-forge/prefer-canonical-length-guardinstead. Their behavior is
unchanged — the rule reuses their implementations rather than reimplementing
them.
eslint-plugin-ts-data-forge@0.3.0
Minor Changes
-
4dfe5d9: The
ArrandStrlength-constrained guards and casts now take their length
arguments first, matching the type-parameter order of the ts-type-forge types
they narrow to, and eachArrone gained a curried form.before after Arr.isMinLengthArray(xs, 3)Arr.isMinLengthArray(3, xs)Arr.isMaxLengthArray(xs, 8)Arr.isMaxLengthArray(8, xs)Arr.isFixedLengthArray(xs, 3)Arr.isFixedLengthArray(3, xs)Arr.isBoundedLengthArray(xs, 1, 5)Arr.isBoundedLengthArray(1, 5, xs)Arr.isFixedLengthTuple(xs, 2)Arr.isFixedLengthTuple(2, xs)Arr.isMinLengthTuple(xs, 1)Arr.isMinLengthTuple(1, xs)Arr.isMaxLengthTuple(xs, 2)Arr.isMaxLengthTuple(2, xs)Arr.isBoundedLengthTuple(xs, 1, 3)Arr.isBoundedLengthTuple(1, 3, xs)Arr.asMinLengthArray(xs, 3)Arr.asMinLengthArray(3, xs)Arr.asMaxLengthArray(xs, 8)Arr.asMaxLengthArray(8, xs)Arr.asFixedLengthArray(xs, 3)Arr.asFixedLengthArray(3, xs)Arr.asBoundedLengthArray(xs, 1, 5)Arr.asBoundedLengthArray(1, 5, xs)Strgets the same treatment, so the two namespaces read alike:before after Str.isMinLengthString(s, 12)Str.isMinLengthString(12, s)Str.isMaxLengthString(s, 32)Str.isMaxLengthString(32, s)Str.isFixedLengthString(s, 2)Str.isFixedLengthString(2, s)Str.isBoundedLengthString(s, 8, 16)Str.isBoundedLengthString(8, 16, s)Str.asMinLengthString(s, 12)Str.asMinLengthString(12, s)Str.asMaxLengthString(s, 32)Str.asMaxLengthString(32, s)Str.asFixedLengthString(s, 2)Str.asFixedLengthString(2, s)Str.asBoundedLengthString(s, 8, 16)Str.asBoundedLengthString(8, 16, s)The type parameters follow the same order — the container (
Xs/S/E)
now comes after the lengths, e.g.Arr.asMinLengthArray<Xs, MinLength>is now
Arr.asMinLengthArray<MinLength, Xs>— so an explicit type argument list reads
in the same order as the call.Omitting the array or string now returns a reusable guard/cast, so the curried
form is plain partial application of the same signature:const hasThree = Arr.isMinLengthArray(3); const isSmallSelection = Arr.isBoundedLengthArray(1, 5); const asRgb = Arr.asFixedLengthArray(3);
The eight
Arr.is*guards gained curried overloads; theas*casts in both
namespaces already had one, and its argument order is unchanged.BREAKING CHANGE: every call to the twenty functions above must swap its
arguments so the length comes first, and an explicit type argument list must
move the container type last. The curriedas*overloads are unaffected.Length-preserving array utilities now keep the branded length constraint of
their input.Arr.mapwas outright broken for a length-branded input:
MinLengthArray<3, E>is an intersection of a tuple and the brand object, which
TypeScript does not treat as an array type, so the homomorphic
Readonly<{ [K in keyof Ar]: B }>mappedlength, the array methods and the
brand keys toBas well and produced a non-array object. It now goes through
ts-type-forge'sChangeArrayElement, which rebuilds the structural part from the
bounds recovered from the brand and re-applies the brand:declare const history: MinLengthArray<3, number>; const labels = Arr.map(history, (n) => `#${n}`); // MinLengthArray<3, string> const first: string = labels[0]; // no `undefined`, as before the map
Arr.toSorted,Arr.toSortedBy,Arr.toReversed,Arr.set,Arr.toUpdated,
Arr.toFilledandArr.toRangeFilledpreviously degraded a branded input to
NonEmptyArray(or a plain array) and now keep the full constraint too.
(Arr.copyalready returned its input type, so it was never affected.)Arr.partition/Arr.chunknow report the bounds of each chunk, and
Arr.create/Arr.newArrayreport an exact length past the tuple range:call before after Arr.partition(xs, 2)readonly E[][]readonly BoundedLengthArray<1, 2, E>[]Arr.create(12, 'x')NonEmptyArray<'x'>FixedLengthArray<12, 'x'>The chunk's lower bound is the point:
partitionnever emits an empty chunk,
so indexed access into one is no longerundefinedat position0.const [firstChunk] = Arr.partition(values, 2); if (firstChunk !== undefined) { const first = firstChunk[0]; // not `| undefined` }
BREAKING CHANGE: a chunk is now branded, so an expected value compared against
one needs the unbranded type spelled out —assert.deepStrictEqual<readonly (readonly number[])[]>(chunks, […]).
Arr.zerosandArr.seqare deliberately unchanged: their argument type only
carries literals up toSmallUint, which they already expand into an exact
tuple, so there is no case left for a brand to describe.The array operations now report the right length constraint for a
length-constrained input, via ts-type-forge'sConstrainedList.Arr.set,Arr.toUpdated,Arr.tail,Arr.butLast,Arr.toReversedand
Arr.zipwere annotated withList.*, which decides what it can say from
whether the input is a fixed-length tuple. A length-constrained array is not one
— itslengthisnumber— soListhanded the input type straight back. For
the operations that shorten an array that is not merely imprecise, it is
wrong:declare const xs: MinLengthArray<5, string>; Arr.tail(xs); // before: MinLengthArray<5, string> — a 4-element result claiming at least five // after: MinLengthArray<4, string>
Arr.setandArr.toUpdatedgain positional precision from the same change:Arr.set([1, 2, 3] as const, 1, 'x'); // before: readonly [1 | 'x', 2 | 'x', 3 | 'x'] // after: readonly [1, 'x', 3]
Arr.toReversedalso drops a hand-rolled workaround
(HasLengthConstraint<Ar> extends true ? ChangeArrayElement<Ar, Ar[number]> : List.Reverse<Ar>)
which kept the length but collapsed the element positions to their union. One
annotation now covers plain tuples and branded arrays alike.A brand intersected with an exact tuple — the shape
Arr.isMinLengthArray(3, xs)
produces — keeps all three of the brand, the exact length and the positions.A union index is handled soundly, upstream.
ArgArrayIndex<Ar>for a tuple
is the union of its indices, so this is reachable from ordinary code:declare const i: 0 | 2; Arr.set([1, 2, 3] as const, i, 'x');
The call replaces one position or the other and never both, so a positional
answer ofreadonly ['x', 2, 'x']is a type that no possible result satisfies.
The cause was upstream:List.SetAtwalked the tuple askingPosition extends I,
which every member of a union answers at once, so all of them were replaced.
ts-type-forge 9.1.1 fixes that at the source, soArr.setandArr.toUpdated
pass the index straight through. Upstream widens only the positions the index can
actually name:Arr.set([1, 2, 3] as const, i, 'x'); // i: 0 | 2 // readonly [1 | 'x', 2, 3 | 'x'] — index 1 is not among `0 | 2`, so it stays `2`
A single literal index keeps the exact positional answer, as before.
Arr.take,Arr.takeLast,Arr.skipandArr.skipLastare sound now, but
not more precise. They used to hand a branded input straight back —
Arr.take(xs, 2)on an "at least 5" array claimed the two-element result still
held at least five. They now return the unconstrainedreadonly E[]for a
branded input, and keep the exactList.*tuple result for a plain array or
tuple.They do not yet use
ConstrainedList.Takeand friends. Against ts-type-forge
9.1.0 they could not: those rebuilt the bounds fromNand could not be
instantiated against a still-genericArat all. 9.1.1 lifts that — it no
longer distributes overN, so the instantiation stays bounded — which makes
propagating the bound through these four possible as a follow-up. It is left out
of this release because it changes their result type for branded inputs and
deserves its own measurement.Arr.toSortedandArr.toSortedBydeliberately keep their existing
annotation. An arbitrary permutation may move any element anywhere, so "same
length, element type widened to the union" stays the honest answer for them.Type-parameter order now follows value-parameter order everywhere. An audit
comparing each type parameter's declaration position against the first value
parameter that mentions it turned up two more signatures out of step, both
reordered:function before after Arr.sumBy(array, mapFn)<N, E><E, N>`Arr.partition(array, c...