Skip to content

Commit

Permalink
update the interface to keep it compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
rawpixel-vincent committed Mar 4, 2024
1 parent d6ec1d9 commit 84ace82
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 57 deletions.
58 changes: 25 additions & 33 deletions StringLiteralList.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { sl } from './types.js';

interface ILiterals<T extends unknown> {
interface ILiterals<T extends string> {
literal: T;
}

export interface IStringList<T extends unknown>
export interface IStringList<T extends string>
extends Omit<
Array<T>,
| sl.specs.ImplementedMethod
| sl.specs.NativeMethodWithTypeOverride
sl.specs.ImplementedMethod
// | sl.specs.NativeMethodWithTypeOverride
>, ILiterals<T> {
// Custom Methods
withPrefix<P extends string>(
Expand All @@ -19,6 +19,8 @@ export interface IStringList<T extends unknown>
): IStringList<sl.utils.StringConcat<T extends string ? T : string, P>>;
value<V = T>(val: V): V extends T ? V : never;
mutable(): T & string[];
sort<P1 = T, P2 = T>(compareFn?: (a: P1, b: P2) => number): this;
reverse(): this;

// Implemented methods to return the frozen array, typed as IStringList.
toSorted(compareFn?: (a: T, b: T) => number): IStringList<T>;
Expand All @@ -29,6 +31,7 @@ export interface IStringList<T extends unknown>
// Readonly overrides
readonly length: number;
readonly [n: number]: T | undefined;
readonly enum: { [P in T & string]: P };

// Supported Methods
at(n: number): T;
Expand All @@ -38,49 +41,40 @@ export interface IStringList<T extends unknown>
indexOf<PP = T>(searchElement: PP, fromIndex?: number): number;
lastIndexOf<PP = T>(searchElement: PP, fromIndex?: number): number;

find<PP = T>(
find<PP = T & string>(
predictate: (
val: PP extends T ? T : PP,
val: PP extends T & string ? T : PP,
i: number,
obj: IStringList<T>,
) => val is PP extends T ? T : PP,
obj: T[]
) => val is PP extends T & string ? T : PP,
): T;
find(
predictate: (
val: string | undefined,
i: number,
obj: IStringList<T>,
obj: T[]
) => boolean,
): T;
findIndex<PP = T>(
predictate: (val: PP extends T ? PP : string) => boolean,
): number;
findIndex(
predictate: (
val: string | undefined,
i: number,
obj: IStringList<T>,
) => boolean,
): number;
findIndex<S = T & string>(predicate: (value: S & string, index: number, obj: T[]) => unknown, thisArg?: any): number;


some<S = T & string>(predicate: (value: S & string, index: number, array: T[]) => unknown, thisArg?: any): boolean;
every<S = T & string>(predicate: (value: S & string, index: number, array: T[]) => value is S & string, thisArg?: any): this is S[];
every(predicate: (value: T & string, index: number, array: T[]) => unknown, thisArg?: any): boolean;

some<PP = T>(
predictate: (val: PP extends T ? PP : string) => boolean,
): boolean;
every<PP = T>(
predictate: (val: PP extends T ? PP : string) => boolean,
): boolean;

// Return Type overrides
with(index: number, value: any): string[];
filter<S extends T>(
predicate: (value: T, index: number, array: IStringList<T>) => value is S,
with<V extends string>(index: number, value: V): (T | V)[];
filter<S = T & string>(
predicate: (value: S & string, index: number, array: T[]) => value is S & string,
thisArg?: any,
): S[];
filter(
predicate: (value: string, index: number, array: IStringList<T>) => boolean,
predicate: (value: string, index: number, array: T[]) => boolean,
thisArg?: any,
): string[];
toSpliced(start: number, deleteCount: number, ...items: string[]): string[];
): T & string[];
toSpliced<S = T & string>(start: number, deleteCount: number, ...items: string[]): (S & string)[];
pop(): T;
}

export class SL<T extends string> {
Expand All @@ -89,14 +83,12 @@ export class SL<T extends string> {

export interface ArrayInPlaceMutation {
push: 'push';
sort: 'sort';
unshift: 'unshift';
shift: 'shift';
copyWithin: 'copyWithin';
pop: 'pop';
fill: 'fill';
splice: 'splice';
reverse: 'reverse';
}

export const ARRAY_IN_PLACE_MUTATION: ArrayInPlaceMutation;
32 changes: 27 additions & 5 deletions StringLiteralList.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class SL extends Array {

// Get the native array
mutable() {
return Array.from(this);
return Array.from(this.values());
}

// Methods returning the native array
Expand Down Expand Up @@ -70,21 +70,43 @@ export class SL extends Array {
const mut = this.mutable();
return mut.with.apply(mut, arguments);
}

enum = new Proxy(
{
self: this,
},
{
get(target, property) {
if (typeof property !== 'string' || !target.self.includes(property)) {
return undefined;
}
return target.self.value(property);
},
},
);
}

export const ARRAY_IN_PLACE_MUTATION = Object.freeze({
push: 'push',
sort: 'sort',
unshift: 'unshift',
shift: 'shift',
copyWithin: 'copyWithin',
pop: 'pop',
fill: 'fill',
splice: 'splice',
reverse: 'reverse',
});

Object.values(ARRAY_IN_PLACE_MUTATION).forEach((el) => {
SL.prototype[el] = () => {
throw new Error(`Array method ${el} is not supported by StringLiteralList`);
SL.prototype[el] = function () {
/* c8 ignore start */
if (
typeof window === 'undefined' &&
process?.env?.NODE_ENV !== 'production' &&
process?.env?.NODE_ENV !== 'test'
) {
console && console.debug(`Array method ${el} is not supported`);
}
/* c8 ignore stop */
return Array.prototype[el].apply(this.mutable(), arguments);
};
});
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "string-literal-list",
"version": "1.5.5",
"version": "1.5.6",
"description": "an array for string literal",
"main": "stringList.js",
"types": "stringList.d.ts",
Expand All @@ -12,11 +12,11 @@
"engines": {
"node": ">=16"
},
"packageManager": "npm@10.5.0",
"packageManager": "npm@10.2.4",
"license": "MIT",
"scripts": {
"test": "npm run lint:ci && npm run test:checkJs && npm run test:unit",
"test:unit": "tap run -R terse && tap report --show-full-coverage",
"test:unit": "NODE_ENV=test tap run && tap report --show-full-coverage",
"test:checkJs": "tsc --checkJs --project ./jsconfig.json",
"prettier": "prettier --write \"**/*.{js,ts}\"",
"lint": "eslint --fix \"./*.js\"",
Expand All @@ -40,6 +40,6 @@
"typescript": "latest"
},
"dependencies": {
"core-js": "^3.36.0"
"core-js": "latest"
}
}
26 changes: 24 additions & 2 deletions stringList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,31 @@ import { SL } from './StringLiteralList.js';

/** @type {import('./stringList.js').stringList} */
export function stringList(...strings) {
if (strings.some((el) => typeof el !== 'string')) {
throw new Error(`Not a string in stringList ${strings[0]}`);
if (strings.length && strings.some((el) => typeof el !== 'string')) {
/* c8 ignore start */
if (
typeof window === 'undefined' &&
process?.env?.NODE_ENV !== 'production' &&
process?.env?.NODE_ENV !== 'test'
) {
console.debug(
'Unexpected type in stringList(). Casting all arguments to string type.',
);
}
/* c8 ignore stop */
return Object.freeze(
new SL(
...strings.flatMap((el) =>
Array.isArray(el)
? el.filter((s) => typeof s === 'string').map((s) => s)
: typeof el === 'string'
? [el]
: [],
),
),
);
}

// @ts-expect-error[2322]
return Object.freeze(new SL(...strings));
}
20 changes: 10 additions & 10 deletions stringList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const testExpectedArrayValues = (st, list, ...values) => {
st.match([...list.entries()], [...values.entries()]);
for (const [i, value] of list.entries()) {
st.match(value, values[i]);
st.match(list.enum[value], value);
st.ok(list.includes(value));
st.ok(list.indexOf(values[i]) === i);
st.ok(list.at(i) === value);
Expand All @@ -35,7 +36,9 @@ const testExpectedArrayValues = (st, list, ...values) => {
st.ok(list.value(value) === value);
}

st.throws(() => list.value(`${Math.random() * 100000}!`));
st.ok(list.enum[`${Math.random() * 100000}!`] === undefined);
st.ok(list.enum[Math.random() * 100000] === undefined);
st.throws(() => list.value(`${Math.random() * 100000}`));

st.notOk(list.includes(null));
st.ok(list.at(values.length) === undefined);
Expand Down Expand Up @@ -263,10 +266,9 @@ t.test('all chained', (t) => {
});

t.test('stringList(invalid arguments) throws', (t) => {
t.throws(
t.doesNotThrow(
// @ts-expect-error[incompatible-call]
() => stringList(4, 'foo'),
new Error('Not a string in stringList 4'),
() => stringList(4, 'foo', ['d', 45], undefined),
);

t.end();
Expand All @@ -275,12 +277,10 @@ t.test('stringList(invalid arguments) throws', (t) => {
t.test('stringList mutable', (t) => {
const list = stringList('foo');
Object.values(ARRAY_IN_PLACE_MUTATION).forEach((el) => {
t.throws(
() =>
// @ts-expect-error
list[el](),
new Error(`Array method ${el} is not supported by StringLiteralList`),
);
t.doesNotThrow(() => {
// @ts-expect-error
list[el](0);
});
// @ts-expect-error
t.ok(list.mutable()[el]());
});
Expand Down
2 changes: 2 additions & 0 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export namespace sl {
| 'withSuffix'
| 'toReversed'
| 'toSorted'
| 'sort'
| 'reverse'
| 'value';

/**
Expand Down

0 comments on commit 84ace82

Please sign in to comment.