Skip to content

Commit

Permalink
issue-#57, #43 - Re-instated use of 'Slice' type for some methods, de…
Browse files Browse the repository at this point in the history
…precated 'Nameable' and 'TypeRef' related types, and added [pseudo] standalone (tsc) tests for 'Slice' type.
  • Loading branch information
elycruz committed Feb 28, 2024
1 parent dff7723 commit 7c2fecd
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 37 deletions.
10 changes: 6 additions & 4 deletions packages/fjl/src/_platform/slice/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Methods that apply to the `Slice` Sum Type (e.g., intersection of `Array` and `String` types).
*/
import {Slice} from "../../types";

export const

/**
Expand All @@ -12,8 +14,8 @@ export const
* method, itself, is actually exported from the library as `append`
* (same as '+' for strings, but for concatables) See '../list/append' module.
*/
concat = (...xss: any[]): any =>
xss.shift()?.concat(...xss),
concat = <T extends Slice>(...xss: (T | ConcatArray<any>)[]): any =>
xss.slice(0, 0).concat(...xss),

/**
* Curried version of `concat`.
Expand Down Expand Up @@ -51,12 +53,12 @@ export const
* Gets item at index; Same as` [].at()` (allows
* negative/right-to-left indexing (see mdn `(Array|String).at` method).
*/
at = (i: number, xs: string | any[]) => xs.at(i),
at = (i: number, xs: Slice) => xs.at(i),

/**
* Curried version of `at`.
*/
$at = (i: number) => (xs: string | any[]) => xs.at(i)
$at = (i: number) => (xs: Slice) => xs.at(i)

;

5 changes: 3 additions & 2 deletions packages/fjl/src/list/group.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {groupBy} from "./groupBy";
import {equal} from "../boolean";
import {Slice} from "../types";

/**
* The group function takes a list and returns a list of lists such that
* the concatenation of the result is equal to the argument. Moreover, each
* sublist in the result contains only equal elements. For example:
*
* ```javascript
* group("Mississippi".slice(0)) === [["M"], ["i"], ["s", "s"], ["i"], ["s", "s"], ["i"], ["p", "p"],[ "i"]]
* group("Mississippi".slice(0)) === [["M"], ["i"], ["s", "s"], ["i"], ["s", "s"], ["i"], ["p", "p"], [ "i"]]
* ```
*/
export const group = (xs: string | any[]): (string | any[])[] => groupBy(equal, xs);
export const group = <T>(xs: Slice<T>): Slice<T>[] => groupBy(equal, xs);
7 changes: 4 additions & 3 deletions packages/fjl/src/list/groupBy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {BinaryPred} from "../types";
import {pushN} from "./pushN";
import {append} from "./append";
import {Slice} from "../types";

export const

Expand All @@ -12,8 +13,8 @@ export const
* [["M"], ["i"], ["s", "s"], ["i"], ["s", "s"], ["i"], ["p", "p"], ["i"]]
* ```
*/
groupBy = <X = string | any, XS extends string | X[] = X[]>(
equalityOp: BinaryPred, xs: XS
groupBy = <X = any, XS extends Slice<X> = Slice<X>>(
equalityOp: BinaryPred<X, X>, xs: XS
): XS[] => {
if (!xs?.length) return [];

Expand Down Expand Up @@ -52,7 +53,7 @@ export const
/**
* Curried version of `$groupBy`.
*/
$groupBy = <X = string | any, XS extends string | X[] = X[]>(
$groupBy = <X = any, XS extends Slice<X> = Slice<X>>(
equalityOp: BinaryPred
) =>
(xs: XS): XS[] =>
Expand Down
4 changes: 3 additions & 1 deletion packages/fjl/src/list/inits.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {Slice} from "../types";

export const
/**
* The inits function returns all initial segments of the argument (shortest first). For example:
Expand All @@ -6,7 +8,7 @@ export const
* shallowEquals(inits('abc'), ['','a','ab','abc'])
* ```
*/
inits = (xs: string | any[]): (typeof xs)[] => {
inits = (xs: Slice): (typeof xs)[] => {
const limit = xs?.length,
agg = [];
if (!limit) return agg;
Expand Down
8 changes: 6 additions & 2 deletions packages/fjl/src/list/last.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import {Slice} from "../types";

/**
* Returns last item of string, and/or, array..
* @deprecated Use `at` instead.
*
* Returns last item of a slice.
*/
export const last = (xs: string | any[]): string | any => xs.at(-1);
export const last = <T = any, TS extends Slice<T> = Slice<T>>(xs: TS): T => xs.at(-1);
17 changes: 10 additions & 7 deletions packages/fjl/src/list/mapAccumR.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {MapAccumOp} from "../types";
import {MapAccumOp, Slice} from "../types";

export const

/**
* Performs a map and a reduce all in one (from right-to-left). Returns a tuple
* containing the aggregated value and the result of mapping the passed in function on passed in list.
* Takes a map reduce function and returns the result of the map (on each item) and the reducePerforms a map and a reduce all in one (from right-to-left) and returns the result of the reduce,
* and the map as a tuple.
*/
mapAccumR = <A, B, C>(
op: MapAccumOp<A, B, C>,
zero: A,
xs: string | B[]
): [A, string | C[]] => {
xs: Slice<B>
): [A, Slice<C>] => {
const limit = xs.length;

if (!limit) return [zero, xs.slice(0) as string | C[]];
if (!limit) return [zero, xs.slice(0) as unknown as Slice<C>];

const mapped = [];

Expand All @@ -28,7 +28,10 @@ export const
return [agg, mapped];
},

/**
* Curried version of `mapAccumR`.
*/
$mapAccumR = <A, B, C>(op: MapAccumOp<A, B, C>) =>
(zero: A) =>
(xs: string | B[]): [A, string | C[]] =>
(xs: Slice<B>): [A, Slice] =>
mapAccumR(op, zero, xs);
3 changes: 3 additions & 0 deletions packages/fjl/src/list/removeBy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const
return sliceCopy(list);
},

/**
* Curried version of `removeBy`.
*/
$removeBy = <T>(pred: BinaryPred<T>) =>
(x: T) =>
(list: Slice<T>): Slice<T> => removeBy(pred, x, list);
4 changes: 3 additions & 1 deletion packages/fjl/src/list/utils/sliceCopy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {Slice} from "../../types";

export const

/**
* Returns a copy of a slice (E.g., an array and/or a string).
*/
sliceCopy = (xs: string | any[]): typeof xs => xs.slice(0)
sliceCopy = (xs: Slice): typeof xs => xs.slice(0)

;
41 changes: 24 additions & 17 deletions packages/fjl/src/types/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@
* General data types used in the library.
*/

/**
* Any type that contains a "readonly" `name` (functions, et al.) property.
*/
export interface Nameable {
readonly name: string;
}

/**
* Represents strings, arrays, and/or, any structures that contain a `length`, and number indices.
*
Expand All @@ -19,23 +12,37 @@ export type NumberIndexable<T = unknown> = ({
[index: number]: T;
} |
/**
* Else support `string`.
* Else support `string`, and other array types.
*/
{
readonly length: number;
readonly [index: number]: any; // Even though string variant type here
// should actually be `string`, this is too strict for
// a Sum type so `any` is used instead - this allows
// `string`, and/or `Array` types, to be used where `NumberIndexable` type is required,
// interchangeably.
readonly [index: number]: any;
});

/**
* `Union + Sum` Slice type - Represents, the intersection, of the string,
* array, and custom structure, types that match the `Slice` "summed" interfaces -
* Basically a type for representing string, and/or "array structure" types.
* The Slice type represents the intersection of string, array, and/or array-like, types.
*/
export interface Slice<T = any> extends Iterable<T> {
readonly length: number;

[index: number]: any;

at(i: number): any; // @note Method not supported by older versions of typescript versions.
concat(...items: (Slice<T> | ConcatArray<any>)[]): this;
indexOf(x: any): number;
includes(x: any): boolean;
lastIndexOf(x: any): number;
slice(start: number, end?: number): this;
}

/**
* @deprecated Use your own type and/or existing native/otherwise types.
*
* Any type that contains a "readonly" `name` (functions, et al.) property.
*/
export type Slice<T = any> = string | T[];
export interface Nameable {
readonly name: string;
}

/**
* @deprecated - Use direct type constructors instead.
Expand Down
10 changes: 10 additions & 0 deletions packages/fjl/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,15 @@ export interface ConstructableType {
// @todo clean this up and allow only one of these types
export type Constructable = ConstructableType
export type TypeConstructor = ConstructableType;

/**
* @deprecated - Associated methods are deprecated, so these types are too -
* Use `ConstructableType` types instead.
*/
export type TypeName = string;

/**
* @deprecated - Associated methods are deprecated, so these types are too -
* Use `ConstructableType` types instead.
*/
export type TypeRef = TypeName | ConstructableType;
46 changes: 46 additions & 0 deletions packages/fjl/tests/list/test-custom-slice-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* General ephemeral tests for `Slice` type - Tests just ensure that TSC (typescript compiler)
* doesn't throw any errors when using the `Slice` type.
*/
import {Slice} from "../../src";

const {stringify} = JSON;

describe('Slice type', () => {
it('Should be able to stand in for `string`, and `T[]` in function types', () => {
const concat = <T extends Slice>(...xss: (T | ConcatArray<any>)[]): any =>
xss[0].slice(0, 0).concat(...xss);

([
[['abc', 'def'], 'abcdef'],
['abcdef'.split('').map(x => [x]), 'abcdef']
] as Slice[])
.forEach(([args, expected]) => {
it(`function context test: concat(${args.map((x: any) => stringify(x)).join(', ')}) === ${stringify(expected)}`, () => {
const result: string = concat(...args); // Ensure return value is inferred to our inline declarations type,
// e.g., no TSC error is thrown

expect(result).toEqual(expected);
});
});
});

it('Should be able to stand in for `string`, and `T[]` types in standalone value declaration contexts;' +
' e.g., it should not throw any TSC errors', () => {
// Should be able to represent string values interchangeably (when calling `concat`, and/or `slice`, methods)
const ctrlHead = 'all your base';
const ctrlTail = 'belong to us';
const someArray: Slice = ctrlHead.split(' ').map(x => x);

expect(ctrlHead.concat(ctrlTail)).toEqual(ctrlHead + ctrlTail);

expect(someArray.concat(...'all your base'.split(' ').map(xs => [xs]))).toEqual(['all', 'your', 'base']);

// Should ignore generic type param when value is a string; E.g., should not throw error
// ----
let someStr2: Slice<string> = 'hi';

someStr2 = ''.slice.call(someStr2, 0);
expect(someStr2).toEqual('hi');
});
});

0 comments on commit 7c2fecd

Please sign in to comment.