diff --git a/.eslintrc b/.eslintrc index 35ee124..9c44b57 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,7 +18,6 @@ "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-var-requires": "warn", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/indent": ["warn", 2], "@typescript-eslint/comma-dangle": "warn", "@typescript-eslint/no-shadow": "warn", "@typescript-eslint/semi": "warn", diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..32ebab4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "none" +} diff --git a/.vscode/settings.json b/.vscode/settings.json index ac0af9a..e8af11f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ "/README.md": true, "/tests/reports": true }, - "editor.renderFinalNewline": false, + "editor.renderFinalNewline": "off", "files.insertFinalNewline": true, "editor.insertSpaces": true, "files.eol": "\n", diff --git a/package.json b/package.json index 8c51bd4..acf2838 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-lib-extended", - "version": "2.0.3", + "version": "2.0.4", "description": "Additional types and tools for typescript", "files": [ "dist" @@ -16,8 +16,8 @@ "test": "vitest run --coverage", "test-ui": "vitest --ui", "test-watch": "vitest watch", - "reset-modules": "npx --quiet rimraf node_modules/ package-lock.json", - "reset-creation": "npx --quiet rimraf dist/ tests/reports/", + "reset-modules": "npx --quiet -y rimraf node_modules/ package-lock.json", + "reset-creation": "npx --quiet -y rimraf dist/ tests/reports/", "reset": "npm run reset-creation && npm run reset-modules", "validate": "npm run reset && npm i && npm run build && npm test", "create": "npm run reset-creation && npm run build && npm test", @@ -47,12 +47,12 @@ "access": "public" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^5.32.0", - "@typescript-eslint/parser": "^5.32.0", - "@vitest/ui": "^0.21.0", - "c8": "^7.12.0", + "@typescript-eslint/eslint-plugin": "^5.60.0", + "@typescript-eslint/parser": "^5.60.0", + "@vitest/coverage-v8": "^0.32.2", + "@vitest/ui": "^0.32.2", "eslint": "^8.21.0", - "typescript": "^4.7.4", - "vitest": "^0.21.0" + "typescript": "^5.1.3", + "vitest": "^0.32.0" } } diff --git a/src/enumerable.ts b/src/enumerable.ts index d43605f..ad3a0c0 100644 --- a/src/enumerable.ts +++ b/src/enumerable.ts @@ -92,7 +92,7 @@ export class EnumerableObject { for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (/^[0-9]$/.exec(key)) { + if (/^[0-9]+$/.exec(key)) { continue; } diff --git a/src/event/index.ts b/src/event/index.ts index a5aefb3..af705e2 100644 --- a/src/event/index.ts +++ b/src/event/index.ts @@ -10,18 +10,22 @@ import type { EventSubscription, EventUnsubscription } from './types'; * @template TArgs */ export class Event { - private _detached: boolean; - private _subscribe: EventSubscription | undefined; - private _unsubscribe: EventUnsubscription | undefined; + private _subscription: + | undefined + | { + subscribe: EventSubscription; + unsubscribe: EventUnsubscription; + }; constructor( subscribe_: EventSubscription, - unsubscrbe_: EventUnsubscription, + unsubscribe_: EventUnsubscription, detachEventProxy_: (detachEvent_: () => void) => void ) { - this._detached = false; - this._subscribe = subscribe_; - this._unsubscribe = unsubscrbe_; + this._subscription = { + subscribe: subscribe_, + unsubscribe: unsubscribe_ + }; detachEventProxy_(() => this.detach()); } @@ -33,7 +37,7 @@ export class Event { * @memberof Event */ public get subscribe(): EventSubscription { - return this.validateDetached(this._subscribe); + return this.validateDetached(this._subscription?.subscribe); } /** @@ -44,11 +48,11 @@ export class Event { * @memberof Event */ public get unsubscribe(): EventUnsubscription { - return this.validateDetached(this._unsubscribe); + return this.validateDetached(this._subscription?.unsubscribe); } private validateDetached(value_: T | undefined): T | never { - if (this._detached || value_ === undefined) { + if (value_ === undefined) { throw new Error('Event is detached!'); } @@ -56,8 +60,6 @@ export class Event { } private detach(): void { - this._detached = true; - this._subscribe = undefined; - this._unsubscribe = undefined; + this._subscription = undefined; } } diff --git a/tests/code/enumerable.test.ts b/tests/code/enumerable.test.ts index f9d28c4..ce17dbf 100644 --- a/tests/code/enumerable.test.ts +++ b/tests/code/enumerable.test.ts @@ -16,31 +16,86 @@ enum StringEnum { b = 'bar' } +enum MoreThan10Items { + a, + b, + c, + d, + e, + f, + g, + h, + i, + j, + k, + l +} + describe(EnumerableObject.name, () => { test('entries', () => { - expect.assertions(3); + expect.assertions(4); const enumObj = new EnumerableObject(); - expect(enumObj.entries(MixedEnum)).toEqual([['a', 'foo'], ['b', 1]]); - expect(enumObj.entries(NumberEnum)).toEqual([['a', 0], ['b', 1]]); - expect(enumObj.entries(StringEnum)).toEqual([['a', 'foo'], ['b', 'bar']]); + expect(enumObj.entries(MixedEnum)).toEqual([ + ['a', 'foo'], + ['b', 1] + ]); + expect(enumObj.entries(NumberEnum)).toEqual([ + ['a', 0], + ['b', 1] + ]); + expect(enumObj.entries(StringEnum)).toEqual([ + ['a', 'foo'], + ['b', 'bar'] + ]); + expect(enumObj.entries(MoreThan10Items)).toEqual([ + ['a', 0], + ['b', 1], + ['c', 2], + ['d', 3], + ['e', 4], + ['f', 5], + ['g', 6], + ['h', 7], + ['i', 8], + ['j', 9], + ['k', 10], + ['l', 11] + ]); }); test('values', () => { - expect.assertions(3); + expect.assertions(4); const enumObj = new EnumerableObject(); expect(enumObj.values(MixedEnum)).toEqual(['foo', 1]); expect(enumObj.values(NumberEnum)).toEqual([0, 1]); expect(enumObj.values(StringEnum)).toEqual(['foo', 'bar']); + expect(enumObj.values(MoreThan10Items)).toEqual([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + ]); }); test('keys', () => { - expect.assertions(3); + expect.assertions(4); const enumObj = new EnumerableObject(); expect(enumObj.keys(MixedEnum)).toEqual(['a', 'b']); expect(enumObj.keys(NumberEnum)).toEqual(['a', 'b']); expect(enumObj.keys(StringEnum)).toEqual(['a', 'b']); + expect(enumObj.keys(MoreThan10Items)).toEqual([ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l' + ]); }); }); diff --git a/tests/code/event.test.ts b/tests/code/event.test.ts index 3186bd4..e415495 100644 --- a/tests/code/event.test.ts +++ b/tests/code/event.test.ts @@ -6,7 +6,10 @@ import { CancelEventArgs } from '../../src/event/args/cancel'; import { EventHandler } from '../../src/event/handler'; class TestSubject extends Disposable { - private _valueChangingHandler: EventHandler>; + private _valueChangingHandler: EventHandler< + this, + CancelEventArgs<{ old: number; new: number }> + >; private _valueChangedHandler: EventHandler>; private _value: number; @@ -21,13 +24,25 @@ class TestSubject extends Disposable { }); } - public get changing(): Event> { return this._valueChangingHandler.event; } + public get changing(): Event< + this, + CancelEventArgs<{ old: number; new: number }> + > { + return this._valueChangingHandler.event; + } - public get changed(): Event> { return this._valueChangedHandler.event; } + public get changed(): Event> { + return this._valueChangedHandler.event; + } - public get value(): number { return this._value; } + public get value(): number { + return this._value; + } public set value(new_: number) { - const cancelArgs = new CancelEventArgs<{ old: number, new: number }>({ old: this._value, new: new_ }); + const cancelArgs = new CancelEventArgs<{ old: number; new: number }>({ + old: this._value, + new: new_ + }); this._valueChangingHandler.invoke(this, cancelArgs); if (cancelArgs.cancel) { @@ -45,22 +60,30 @@ function createTestSubject(value_: number): TestSubject { describe(TestSubject.name, () => { test('value', () => { - expect.assertions(25); + expect.assertions(27); const subject = createTestSubject(42); let changingCount = 0; let changedCount = 0; - expect(subject.changing.subscribe('changing', (sender_, args_) => { - expect(subject === sender_).toBe(true); - changingCount++; - args_.cancel = args_.value.new === 21; - })).toBe(true); - expect(subject.changed.subscribe('changed', (sender_, args_) => { - expect(subject === sender_).toBe(true); - changedCount++; - expect(args_.value).toBe(123456789); - })).toBe(true); + expect( + subject.changing.subscribe('changing', (sender_, args_) => { + expect(subject === sender_).toBe(true); + changingCount++; + args_.cancel = args_.value.new === 21; + }) + ).toBe(true); + expect( + subject.changed.subscribe('changed', (sender_, args_) => { + expect(subject === sender_).toBe(true); + changedCount++; + expect(args_.value).toBe(123456789); + }) + ).toBe(true); - expect(subject.changing.subscribe('changing', () => { /** */ })).toBe(false); + expect( + subject.changing.subscribe('changing', () => { + /** */ + }) + ).toBe(false); expect(changingCount).toBe(0); expect(changedCount).toBe(0); @@ -82,10 +105,34 @@ describe(TestSubject.name, () => { expect(changedCount).toBe(1); expect(subject.value).toBe(21); + const changingEventBeforeDispose = subject.changing; + const changedEventBeforeDispose = subject.changed; subject.dispose(); - expect(() => subject.changing.subscribe('x', () => { /** */ })).toThrow(); - expect(() => subject.changed.subscribe('y', () => { /** */ })).toThrow(); + expect(() => { + // will throw on getting "changing" event + subject.changing.subscribe('x', () => { + /** */ + }); + }).toThrow(); + expect(() => { + // will throw on subscribing detached event + changingEventBeforeDispose.subscribe('x', () => { + /** */ + }); + }).toThrow(); + expect(() => + // will throw on getting "changed" event + subject.changed.subscribe('y', () => { + /** */ + }) + ).toThrow(); + expect(() => { + // will throw on subscribing detached event + changedEventBeforeDispose.subscribe('x', () => { + /** */ + }); + }).toThrow(); expect(() => subject.changing.unsubscribe('x')).toThrow(); expect(() => subject.changed.unsubscribe('y')).toThrow(); });