From 8dc0c4358419f33d9b5974bd10b53049d3a446b8 Mon Sep 17 00:00:00 2001 From: Alexandre VICTOOR Date: Sat, 4 Jan 2025 19:00:40 +0100 Subject: [PATCH] refacto: avoid return in constructors --- __tests__/List.ts | 2 +- __tests__/groupBy.ts | 6 +-- src/Collection.js | 52 ++++++++---------- src/CollectionImpl.js | 121 ++++++++++++++++++++++-------------------- src/List.js | 67 ++++++++++++----------- src/Map.js | 30 +++++------ src/Operations.js | 29 ++++++---- src/OrderedMap.js | 42 +++++++-------- src/OrderedSet.js | 53 +++++++++--------- src/Range.js | 51 ++++++++---------- src/Record.js | 1 + src/Repeat.js | 29 +++++----- src/Seq.js | 105 +++++++++++++++++------------------- src/Set.js | 71 +++++++++++++------------ src/Stack.js | 30 +++++------ src/methods/merge.js | 2 +- 16 files changed, 341 insertions(+), 350 deletions(-) diff --git a/__tests__/List.ts b/__tests__/List.ts index 9b40b99d38..d532037a89 100644 --- a/__tests__/List.ts +++ b/__tests__/List.ts @@ -111,7 +111,7 @@ describe('List', () => { expect(out.getIn(['c', 0])).toEqual('v'); expect(out.get('a')).toBeInstanceOf(Array); expect(out.get('b')).toBeInstanceOf(Array); - expect(out.get('c')).toBeInstanceOf(Map); + expect(Map.isMap(out.get('c'))).toBe(true); expect(out.get('c')?.keySeq().first()).toBe(0); }); diff --git a/__tests__/groupBy.ts b/__tests__/groupBy.ts index 4235721e07..e3c388a8fe 100644 --- a/__tests__/groupBy.ts +++ b/__tests__/groupBy.ts @@ -34,15 +34,15 @@ describe('groupBy', () => { const grouped = col.groupBy(v => v); // all groupBy should be instance of Map - expect(grouped).toBeInstanceOf(Map); + expect(Map.isMap(grouped)).toBe(true); // ordered objects should be instance of OrderedMap expect(isOrdered(col)).toBe(constructorIsOrdered); expect(isOrdered(grouped)).toBe(constructorIsOrdered); if (constructorIsOrdered) { - expect(grouped).toBeInstanceOf(OrderedMap); + expect(OrderedMap.isOrderedMap(grouped)).toBe(true); } else { - expect(grouped).not.toBeInstanceOf(OrderedMap); + expect(OrderedMap.isOrderedMap(grouped)).toBe(false); } } ); diff --git a/src/Collection.js b/src/Collection.js index 650ff654ff..e8eff14aac 100644 --- a/src/Collection.js +++ b/src/Collection.js @@ -4,34 +4,24 @@ import { isKeyed } from './predicates/isKeyed'; import { isIndexed } from './predicates/isIndexed'; import { isAssociative } from './predicates/isAssociative'; -export class Collection { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return isCollection(value) ? value : Seq(value); - } -} - -export class KeyedCollection extends Collection { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return isKeyed(value) ? value : KeyedSeq(value); - } -} - -export class IndexedCollection extends Collection { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return isIndexed(value) ? value : IndexedSeq(value); - } -} - -export class SetCollection extends Collection { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return isCollection(value) && !isAssociative(value) ? value : SetSeq(value); - } -} - -Collection.Keyed = KeyedCollection; -Collection.Indexed = IndexedCollection; -Collection.Set = SetCollection; +export const Collection = value => (isCollection(value) ? value : Seq(value)); +export class CollectionImpl {} + +export const KeyedCollection = value => + isKeyed(value) ? value : KeyedSeq(value); + +export class KeyedCollectionImpl extends CollectionImpl {} + +export const IndexedCollection = value => + isIndexed(value) ? value : IndexedSeq(value); + +export class IndexedCollectionImpl extends CollectionImpl {} + +export const SetCollection = value => + isCollection(value) && !isAssociative(value) ? value : SetSeq(value); + +export class SetCollectionImpl extends CollectionImpl {} + +Collection.Keyed = KeyedCollectionImpl; +Collection.Indexed = IndexedCollectionImpl; +Collection.Set = SetCollectionImpl; diff --git a/src/CollectionImpl.js b/src/CollectionImpl.js index ac5ba8011d..50000dc601 100644 --- a/src/CollectionImpl.js +++ b/src/CollectionImpl.js @@ -1,30 +1,31 @@ import { Collection, - KeyedCollection, - IndexedCollection, - SetCollection, + CollectionImpl, + IndexedCollectionImpl, + KeyedCollectionImpl, + SetCollectionImpl, } from './Collection'; -import { IS_COLLECTION_SYMBOL } from './predicates/isCollection'; -import { isKeyed, IS_KEYED_SYMBOL } from './predicates/isKeyed'; -import { isIndexed, IS_INDEXED_SYMBOL } from './predicates/isIndexed'; -import { isOrdered, IS_ORDERED_SYMBOL } from './predicates/isOrdered'; -import { is } from './is'; -import { - NOT_SET, - ensureSize, - wrapIndex, - returnTrue, - resolveBegin, -} from './TrieUtils'; import { hash } from './Hash'; -import { imul, smi } from './Math'; +import { is } from './is'; import { - Iterator, - ITERATOR_SYMBOL, + ITERATE_ENTRIES, ITERATE_KEYS, ITERATE_VALUES, - ITERATE_ENTRIES, + Iterator, + ITERATOR_SYMBOL, } from './Iterator'; +import { imul, smi } from './Math'; +import { IS_COLLECTION_SYMBOL } from './predicates/isCollection'; +import { IS_INDEXED_SYMBOL, isIndexed } from './predicates/isIndexed'; +import { IS_KEYED_SYMBOL, isKeyed } from './predicates/isKeyed'; +import { IS_ORDERED_SYMBOL, isOrdered } from './predicates/isOrdered'; +import { + ensureSize, + NOT_SET, + resolveBegin, + returnTrue, + wrapIndex, +} from './TrieUtils'; import arrCopy from './utils/arrCopy'; import assertNotInfinite from './utils/assertNotInfinite'; @@ -32,48 +33,54 @@ import deepEqual from './utils/deepEqual'; import mixin from './utils/mixin'; import quoteString from './utils/quoteString'; -import { toJS } from './toJS'; -import { Map } from './Map'; -import { OrderedMap } from './OrderedMap'; import { List } from './List'; -import { Set } from './Set'; -import { OrderedSet } from './OrderedSet'; -import { Stack } from './Stack'; -import { Range } from './Range'; -import { KeyedSeq, IndexedSeq, SetSeq, ArraySeq } from './Seq'; +import { Map } from './Map'; +import { getIn } from './methods/getIn'; +import { hasIn } from './methods/hasIn'; +import { toObject } from './methods/toObject'; import { - reify, - ToKeyedSequence, - ToIndexedSequence, - ToSetSequence, - FromEntriesSequence, + concatFactory, + countByFactory, + filterFactory, + flatMapFactory, + flattenFactory, flipFactory, + FromEntriesSequence, + groupByFactory, + interposeFactory, mapFactory, + maxFactory, + partitionFactory, + reify, reverseFactory, - filterFactory, - countByFactory, - groupByFactory, - sliceFactory, - takeWhileFactory, skipWhileFactory, - concatFactory, - flattenFactory, - flatMapFactory, - interposeFactory, + sliceFactory, sortFactory, - maxFactory, + takeWhileFactory, + ToIndexedSequence, + ToKeyedSequence, + ToSetSequence, zipWithFactory, - partitionFactory, } from './Operations'; -import { getIn } from './methods/getIn'; -import { hasIn } from './methods/hasIn'; -import { toObject } from './methods/toObject'; +import { OrderedMap } from './OrderedMap'; +import { OrderedSet } from './OrderedSet'; +import { Range } from './Range'; +import { + ArraySeq, + IndexedSeq, + IndexedSeqImpl, + KeyedSeqImpl, + SetSeqImpl, +} from './Seq'; +import { Set } from './Set'; +import { Stack } from './Stack'; +import { toJS } from './toJS'; export { Collection, CollectionPrototype, IndexedCollectionPrototype }; Collection.Iterator = Iterator; -mixin(Collection, { +mixin(CollectionImpl, { // ### Conversion to other types toArray() { @@ -489,7 +496,7 @@ mixin(Collection, { // abstract __iterator(type, reverse) }); -const CollectionPrototype = Collection.prototype; +const CollectionPrototype = CollectionImpl.prototype; CollectionPrototype[IS_COLLECTION_SYMBOL] = true; CollectionPrototype[ITERATOR_SYMBOL] = CollectionPrototype.values; CollectionPrototype.toJSON = CollectionPrototype.toArray; @@ -500,7 +507,7 @@ CollectionPrototype.inspect = CollectionPrototype.toSource = function () { CollectionPrototype.chain = CollectionPrototype.flatMap; CollectionPrototype.contains = CollectionPrototype.includes; -mixin(KeyedCollection, { +mixin(KeyedCollectionImpl, { // ### More sequential methods flip() { @@ -528,14 +535,14 @@ mixin(KeyedCollection, { }, }); -const KeyedCollectionPrototype = KeyedCollection.prototype; +const KeyedCollectionPrototype = KeyedCollectionImpl.prototype; KeyedCollectionPrototype[IS_KEYED_SYMBOL] = true; KeyedCollectionPrototype[ITERATOR_SYMBOL] = CollectionPrototype.entries; KeyedCollectionPrototype.toJSON = toObject; KeyedCollectionPrototype.__toStringMapper = (v, k) => quoteString(k) + ': ' + quoteString(v); -mixin(IndexedCollection, { +mixin(IndexedCollectionImpl, { // ### Conversion to other types toKeyedSeq() { @@ -667,11 +674,11 @@ mixin(IndexedCollection, { }, }); -const IndexedCollectionPrototype = IndexedCollection.prototype; +const IndexedCollectionPrototype = IndexedCollectionImpl.prototype; IndexedCollectionPrototype[IS_INDEXED_SYMBOL] = true; IndexedCollectionPrototype[IS_ORDERED_SYMBOL] = true; -mixin(SetCollection, { +mixin(SetCollectionImpl, { // ### ES6 Collection methods (ES6 Array and Map) get(value, notSetValue) { @@ -689,16 +696,16 @@ mixin(SetCollection, { }, }); -const SetCollectionPrototype = SetCollection.prototype; +const SetCollectionPrototype = SetCollectionImpl.prototype; SetCollectionPrototype.has = CollectionPrototype.includes; SetCollectionPrototype.contains = SetCollectionPrototype.includes; SetCollectionPrototype.keys = SetCollectionPrototype.values; // Mixin subclasses -mixin(KeyedSeq, KeyedCollectionPrototype); -mixin(IndexedSeq, IndexedCollectionPrototype); -mixin(SetSeq, SetCollectionPrototype); +mixin(KeyedSeqImpl, KeyedCollectionPrototype); +mixin(IndexedSeqImpl, IndexedCollectionPrototype); +mixin(SetSeqImpl, SetCollectionPrototype); // #pragma Helper functions diff --git a/src/List.js b/src/List.js index 26a946ece2..2e6e43a76b 100644 --- a/src/List.js +++ b/src/List.js @@ -12,7 +12,7 @@ import { resolveEnd, } from './TrieUtils'; import { IS_LIST_SYMBOL, isList } from './predicates/isList'; -import { IndexedCollection } from './Collection'; +import { IndexedCollectionImpl, IndexedCollection } from './Collection'; import { hasIterator, Iterator, iteratorValue, iteratorDone } from './Iterator'; import { setIn } from './methods/setIn'; import { deleteIn } from './methods/deleteIn'; @@ -26,39 +26,38 @@ import { asImmutable } from './methods/asImmutable'; import { wasAltered } from './methods/wasAltered'; import assertNotInfinite from './utils/assertNotInfinite'; -export class List extends IndexedCollection { - // @pragma Construction - - constructor(value) { - const empty = emptyList(); - if (value === undefined || value === null) { - // eslint-disable-next-line no-constructor-return - return empty; - } - if (isList(value)) { - // eslint-disable-next-line no-constructor-return - return value; - } - const iter = IndexedCollection(value); - const size = iter.size; - if (size === 0) { - // eslint-disable-next-line no-constructor-return - return empty; - } - assertNotInfinite(size); - if (size > 0 && size < SIZE) { - // eslint-disable-next-line no-constructor-return - return makeList(0, size, SHIFT, null, new VNode(iter.toArray())); - } - // eslint-disable-next-line no-constructor-return - return empty.withMutations(list => { - list.setSize(size); - iter.forEach((v, i) => list.set(i, v)); - }); +export const List = value => { + const empty = emptyList(); + if (value === undefined || value === null) { + return empty; + } + if (isList(value)) { + return value; } + const iter = IndexedCollection(value); + const size = iter.size; + if (size === 0) { + return empty; + } + assertNotInfinite(size); + if (size > 0 && size < SIZE) { + return makeList(0, size, SHIFT, null, new VNode(iter.toArray())); + } + return empty.withMutations(list => { + list.setSize(size); + iter.forEach((v, i) => list.set(i, v)); + }); +}; + +List.of = function (/*...values*/) { + return List(arguments); +}; + +export class ListImpl extends IndexedCollectionImpl { + // @pragma Construction - static of(/*...values*/) { - return this(arguments); + create(value) { + return List(value); } toString() { @@ -159,7 +158,7 @@ export class List extends IndexedCollection { return this; } if (this.size === 0 && !this.__ownerID && seqs.length === 1) { - return this.constructor(seqs[0]); + return List(seqs[0]); } return this.withMutations(list => { seqs.forEach(seq => seq.forEach(value => list.push(value))); @@ -241,7 +240,7 @@ export class List extends IndexedCollection { List.isList = isList; -const ListPrototype = List.prototype; +const ListPrototype = ListImpl.prototype; ListPrototype[IS_LIST_SYMBOL] = true; ListPrototype[DELETE] = ListPrototype.remove; ListPrototype.merge = ListPrototype.concat; diff --git a/src/Map.js b/src/Map.js index 6b9e765e7a..f3c17b4c53 100644 --- a/src/Map.js +++ b/src/Map.js @@ -1,5 +1,5 @@ import { is } from './is'; -import { Collection, KeyedCollection } from './Collection'; +import { Collection, KeyedCollection, KeyedCollectionImpl } from './Collection'; import { IS_MAP_SYMBOL, isMap } from './predicates/isMap'; import { isOrdered } from './predicates/isOrdered'; import { @@ -32,20 +32,20 @@ import { wasAltered } from './methods/wasAltered'; import { OrderedMap } from './OrderedMap'; -export class Map extends KeyedCollection { - // @pragma Construction +export const Map = value => + value === undefined || value === null + ? emptyMap() + : isMap(value) && !isOrdered(value) + ? value + : emptyMap().withMutations(map => { + const iter = KeyedCollection(value); + assertNotInfinite(iter.size); + iter.forEach((v, k) => map.set(k, v)); + }); - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptyMap() - : isMap(value) && !isOrdered(value) - ? value - : emptyMap().withMutations(map => { - const iter = KeyedCollection(value); - assertNotInfinite(iter.size); - iter.forEach((v, k) => map.set(k, v)); - }); +export class MapImpl extends KeyedCollectionImpl { + create(value) { + return Map(value); } toString() { @@ -150,7 +150,7 @@ export class Map extends KeyedCollection { Map.isMap = isMap; -const MapPrototype = Map.prototype; +const MapPrototype = MapImpl.prototype; MapPrototype[IS_MAP_SYMBOL] = true; MapPrototype[DELETE] = MapPrototype.remove; MapPrototype.removeAll = MapPrototype.deleteAll; diff --git a/src/Operations.js b/src/Operations.js index f50857a064..e10cb64135 100644 --- a/src/Operations.js +++ b/src/Operations.js @@ -27,19 +27,22 @@ import { ITERATE_ENTRIES, } from './Iterator'; import { - Seq, + SeqImpl, KeyedSeq, SetSeq, IndexedSeq, keyedSeqFromValue, indexedSeqFromValue, ArraySeq, + KeyedSeqImpl, + IndexedSeqImpl, + SetSeqImpl, } from './Seq'; import { Map } from './Map'; import { OrderedMap } from './OrderedMap'; -export class ToKeyedSequence extends KeyedSeq { +export class ToKeyedSequence extends KeyedSeqImpl { constructor(indexed, useKeys) { this._iter = indexed; this._useKeys = useKeys; @@ -84,7 +87,7 @@ export class ToKeyedSequence extends KeyedSeq { } ToKeyedSequence.prototype[IS_ORDERED_SYMBOL] = true; -export class ToIndexedSequence extends IndexedSeq { +export class ToIndexedSequence extends IndexedSeqImpl { constructor(iter) { this._iter = iter; this.size = iter.size; @@ -121,7 +124,7 @@ export class ToIndexedSequence extends IndexedSeq { } } -export class ToSetSequence extends SetSeq { +export class ToSetSequence extends SetSeqImpl { constructor(iter) { this._iter = iter; this.size = iter.size; @@ -146,7 +149,7 @@ export class ToSetSequence extends SetSeq { } } -export class FromEntriesSequence extends KeyedSeq { +export class FromEntriesSequence extends KeyedSeqImpl { constructor(entries) { this._iter = entries; this.size = entries.size; @@ -845,7 +848,13 @@ export function zipWithFactory(keyIter, zipper, iters, zipAll) { // #pragma Helper Functions export function reify(iter, seq) { - return iter === seq ? iter : isSeq(iter) ? seq : iter.constructor(seq); + return iter === seq + ? iter + : isSeq(iter) + ? seq + : iter.create + ? iter.create(seq) + : iter.constructor(seq); } function validateEntry(entry) { @@ -865,10 +874,10 @@ function collectionClass(collection) { function makeSequence(collection) { return Object.create( (isKeyed(collection) - ? KeyedSeq + ? KeyedSeqImpl : isIndexed(collection) - ? IndexedSeq - : SetSeq + ? IndexedSeqImpl + : SetSeqImpl ).prototype ); } @@ -879,7 +888,7 @@ function cacheResultThrough() { this.size = this._iter.size; return this; } - return Seq.prototype.cacheResult.call(this); + return SeqImpl.prototype.cacheResult.call(this); } function defaultComparator(a, b) { diff --git a/src/OrderedMap.js b/src/OrderedMap.js index 5b0791a851..1d2df63788 100644 --- a/src/OrderedMap.js +++ b/src/OrderedMap.js @@ -1,29 +1,27 @@ import { KeyedCollection } from './Collection'; import { IS_ORDERED_SYMBOL } from './predicates/isOrdered'; import { isOrderedMap } from './predicates/isOrderedMap'; -import { Map, emptyMap } from './Map'; +import { MapImpl, emptyMap } from './Map'; import { emptyList } from './List'; import { DELETE, NOT_SET, SIZE } from './TrieUtils'; import assertNotInfinite from './utils/assertNotInfinite'; -export class OrderedMap extends Map { - // @pragma Construction - - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptyOrderedMap() - : isOrderedMap(value) - ? value - : emptyOrderedMap().withMutations(map => { - const iter = KeyedCollection(value); - assertNotInfinite(iter.size); - iter.forEach((v, k) => map.set(k, v)); - }); - } - - static of(/*...values*/) { - return this(arguments); +export const OrderedMap = value => + value === undefined || value === null + ? emptyOrderedMap() + : isOrderedMap(value) + ? value + : emptyOrderedMap().withMutations(map => { + const iter = KeyedCollection(value); + assertNotInfinite(iter.size); + iter.forEach((v, k) => map.set(k, v)); + }); +OrderedMap.of = function (/*...values*/) { + return OrderedMap(arguments); +}; +export class OrderedMapImpl extends MapImpl { + create(value) { + return OrderedMap(value); } toString() { @@ -94,11 +92,11 @@ export class OrderedMap extends Map { OrderedMap.isOrderedMap = isOrderedMap; -OrderedMap.prototype[IS_ORDERED_SYMBOL] = true; -OrderedMap.prototype[DELETE] = OrderedMap.prototype.remove; +OrderedMapImpl.prototype[IS_ORDERED_SYMBOL] = true; +OrderedMapImpl.prototype[DELETE] = OrderedMapImpl.prototype.remove; function makeOrderedMap(map, list, ownerID, hash) { - const omap = Object.create(OrderedMap.prototype); + const omap = Object.create(OrderedMapImpl.prototype); omap.size = map ? map.size : 0; omap._map = map; omap._list = list; diff --git a/src/OrderedSet.js b/src/OrderedSet.js index 94dbfb4fb2..2a81570756 100644 --- a/src/OrderedSet.js +++ b/src/OrderedSet.js @@ -1,33 +1,32 @@ -import { SetCollection, KeyedCollection } from './Collection'; -import { IS_ORDERED_SYMBOL } from './predicates/isOrdered'; -import { isOrderedSet } from './predicates/isOrderedSet'; +import { KeyedCollection, SetCollection } from './Collection'; import { IndexedCollectionPrototype } from './CollectionImpl'; -import { Set } from './Set'; import { emptyOrderedMap } from './OrderedMap'; +import { IS_ORDERED_SYMBOL } from './predicates/isOrdered'; +import { isOrderedSet } from './predicates/isOrderedSet'; +import { SetImpl } from './Set'; import assertNotInfinite from './utils/assertNotInfinite'; -export class OrderedSet extends Set { - // @pragma Construction - - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptyOrderedSet() - : isOrderedSet(value) - ? value - : emptyOrderedSet().withMutations(set => { - const iter = SetCollection(value); - assertNotInfinite(iter.size); - iter.forEach(v => set.add(v)); - }); - } - - static of(/*...values*/) { - return this(arguments); - } - - static fromKeys(value) { - return this(KeyedCollection(value).keySeq()); +export const OrderedSet = value => + value === undefined || value === null + ? emptyOrderedSet() + : isOrderedSet(value) + ? value + : emptyOrderedSet().withMutations(set => { + const iter = SetCollection(value); + assertNotInfinite(iter.size); + iter.forEach(v => set.add(v)); + }); + +OrderedSet.of = function (/*...values*/) { + return OrderedSet(arguments); +}; + +OrderedSet.fromKeys = function (value) { + return OrderedSet(KeyedCollection(value).keySeq()); +}; +export class OrderedSetImpl extends SetImpl { + create(value) { + return OrderedSet(value); } toString() { @@ -37,7 +36,7 @@ export class OrderedSet extends Set { OrderedSet.isOrderedSet = isOrderedSet; -const OrderedSetPrototype = OrderedSet.prototype; +const OrderedSetPrototype = OrderedSetImpl.prototype; OrderedSetPrototype[IS_ORDERED_SYMBOL] = true; OrderedSetPrototype.zip = IndexedCollectionPrototype.zip; OrderedSetPrototype.zipWith = IndexedCollectionPrototype.zipWith; diff --git a/src/Range.js b/src/Range.js index 28ddd08bcc..23e164a429 100644 --- a/src/Range.js +++ b/src/Range.js @@ -1,5 +1,5 @@ import { wrapIndex, wholeSlice, resolveBegin, resolveEnd } from './TrieUtils'; -import { IndexedSeq } from './Seq'; +import { IndexedSeqImpl } from './Seq'; import { Iterator, iteratorValue, iteratorDone } from './Iterator'; import invariant from './utils/invariant'; @@ -10,38 +10,33 @@ import deepEqual from './utils/deepEqual'; * (exclusive), by step, where start defaults to 0, step to 1, and end to * infinity. When start is equal to end, returns empty list. */ -export class Range extends IndexedSeq { - constructor(start, end, step = 1) { - if (!(this instanceof Range)) { - // eslint-disable-next-line no-constructor-return - return new Range(start, end, step); - } - invariant(step !== 0, 'Cannot step a Range by 0'); - invariant( - start !== undefined, - 'You must define a start value when using Range' - ); - invariant( - end !== undefined, - 'You must define an end value when using Range' - ); +export const Range = (start, end, step = 1) => { + invariant(step !== 0, 'Cannot step a Range by 0'); + invariant( + start !== undefined, + 'You must define a start value when using Range' + ); + invariant(end !== undefined, 'You must define an end value when using Range'); - step = Math.abs(step); - if (end < start) { - step = -step; + step = Math.abs(step); + if (end < start) { + step = -step; + } + const size = Math.max(0, Math.ceil((end - start) / step - 1) + 1); + if (size === 0) { + if (!EMPTY_RANGE) { + EMPTY_RANGE = new RangeImpl(start, end, step, 0); } + return EMPTY_RANGE; + } + return new RangeImpl(start, end, step, size); +}; +export class RangeImpl extends IndexedSeqImpl { + constructor(start, end, step, size) { this._start = start; this._end = end; this._step = step; - this.size = Math.max(0, Math.ceil((end - start) / step - 1) + 1); - if (this.size === 0) { - if (EMPTY_RANGE) { - // eslint-disable-next-line no-constructor-return - return EMPTY_RANGE; - } - // eslint-disable-next-line @typescript-eslint/no-this-alias - EMPTY_RANGE = this; - } + this.size = size; } toString() { diff --git a/src/Record.js b/src/Record.js index 96d8ae4303..8058dda1b7 100644 --- a/src/Record.js +++ b/src/Record.js @@ -99,6 +99,7 @@ export class Record { const RecordTypePrototype = (RecordType.prototype = Object.create(RecordPrototype)); RecordTypePrototype.constructor = RecordType; + RecordTypePrototype.create = RecordType; if (name) { RecordType.displayName = name; diff --git a/src/Repeat.js b/src/Repeat.js index 0fb106156c..bc20177523 100644 --- a/src/Repeat.js +++ b/src/Repeat.js @@ -1,5 +1,5 @@ import { wholeSlice, resolveBegin, resolveEnd } from './TrieUtils'; -import { IndexedSeq } from './Seq'; +import { IndexedSeqImpl } from './Seq'; import { is } from './is'; import { Iterator, iteratorValue, iteratorDone } from './Iterator'; @@ -9,22 +9,21 @@ import deepEqual from './utils/deepEqual'; * Returns a lazy Seq of `value` repeated `times` times. When `times` is * undefined, returns an infinite sequence of `value`. */ -export class Repeat extends IndexedSeq { - constructor(value, times) { - if (!(this instanceof Repeat)) { - // eslint-disable-next-line no-constructor-return - return new Repeat(value, times); +export const Repeat = (value, times) => { + const size = times === undefined ? Infinity : Math.max(0, times); + if (size === 0) { + if (!EMPTY_REPEAT) { + EMPTY_REPEAT = new RepeatImpl(value, 0); } + return EMPTY_REPEAT; + } + return new RepeatImpl(value, size); +}; + +export class RepeatImpl extends IndexedSeqImpl { + constructor(value, size) { this._value = value; - this.size = times === undefined ? Infinity : Math.max(0, times); - if (this.size === 0) { - if (EMPTY_REPEAT) { - // eslint-disable-next-line no-constructor-return - return EMPTY_REPEAT; - } - // eslint-disable-next-line @typescript-eslint/no-this-alias - EMPTY_REPEAT = this; - } + this.size = size; } toString() { diff --git a/src/Seq.js b/src/Seq.js index c1759d9553..2afcaa8482 100644 --- a/src/Seq.js +++ b/src/Seq.js @@ -1,5 +1,5 @@ import { wrapIndex } from './TrieUtils'; -import { Collection } from './Collection'; +import { CollectionImpl } from './Collection'; import { IS_SEQ_SYMBOL, isSeq } from './predicates/isSeq'; import { isImmutable } from './predicates/isImmutable'; import { isCollection } from './predicates/isCollection'; @@ -21,16 +21,13 @@ import { import hasOwnProperty from './utils/hasOwnProperty'; import isArrayLike from './utils/isArrayLike'; -export class Seq extends Collection { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptySequence() - : isImmutable(value) - ? value.toSeq() - : seqFromValue(value); - } - +export const Seq = value => + value === undefined || value === null + ? emptySequence() + : isImmutable(value) + ? value.toSeq() + : seqFromValue(value); +export class SeqImpl extends CollectionImpl { toSeq() { return this; } @@ -84,43 +81,36 @@ export class Seq extends Collection { } } -export class KeyedSeq extends Seq { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptySequence().toKeyedSeq() - : isCollection(value) - ? isKeyed(value) - ? value.toSeq() - : value.fromEntrySeq() - : isRecord(value) +export const KeyedSeq = value => + value === undefined || value === null + ? emptySequence().toKeyedSeq() + : isCollection(value) + ? isKeyed(value) ? value.toSeq() - : keyedSeqFromValue(value); - } - + : value.fromEntrySeq() + : isRecord(value) + ? value.toSeq() + : keyedSeqFromValue(value); +export class KeyedSeqImpl extends SeqImpl { toKeyedSeq() { return this; } } -export class IndexedSeq extends Seq { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptySequence() - : isCollection(value) - ? isKeyed(value) - ? value.entrySeq() - : value.toIndexedSeq() - : isRecord(value) - ? value.toSeq().entrySeq() - : indexedSeqFromValue(value); - } - - static of(/*...values*/) { - return IndexedSeq(arguments); - } - +export const IndexedSeq = value => + value === undefined || value === null + ? emptySequence() + : isCollection(value) + ? isKeyed(value) + ? value.entrySeq() + : value.toIndexedSeq() + : isRecord(value) + ? value.toSeq().entrySeq() + : indexedSeqFromValue(value); +IndexedSeq.of = function (/*...values*/) { + return IndexedSeq(arguments); +}; +export class IndexedSeqImpl extends SeqImpl { toIndexedSeq() { return this; } @@ -129,19 +119,17 @@ export class IndexedSeq extends Seq { return this.__toString('Seq [', ']'); } } +export const SetSeq = value => + (isCollection(value) && !isAssociative(value) + ? value + : IndexedSeq(value) + ).toSetSeq(); -export class SetSeq extends Seq { - constructor(value) { - // eslint-disable-next-line no-constructor-return - return ( - isCollection(value) && !isAssociative(value) ? value : IndexedSeq(value) - ).toSetSeq(); - } - - static of(/*...values*/) { - return SetSeq(arguments); - } +SetSeq.of = function (/*...values*/) { + return SetSeq(arguments); +}; +export class SetSeqImpl extends SeqImpl { toSetSeq() { return this; } @@ -152,12 +140,13 @@ Seq.Keyed = KeyedSeq; Seq.Set = SetSeq; Seq.Indexed = IndexedSeq; -Seq.prototype[IS_SEQ_SYMBOL] = true; +SeqImpl.prototype[IS_SEQ_SYMBOL] = true; // #pragma Root Sequences -export class ArraySeq extends IndexedSeq { +export class ArraySeq extends IndexedSeqImpl { constructor(array) { + super(); this._array = array; this.size = array.length; } @@ -193,8 +182,9 @@ export class ArraySeq extends IndexedSeq { } } -class ObjectSeq extends KeyedSeq { +class ObjectSeq extends KeyedSeqImpl { constructor(object) { + super(); const keys = Object.keys(object).concat( Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(object) : [] ); @@ -244,8 +234,9 @@ class ObjectSeq extends KeyedSeq { } ObjectSeq.prototype[IS_ORDERED_SYMBOL] = true; -class CollectionSeq extends IndexedSeq { +class CollectionSeq extends IndexedSeqImpl { constructor(collection) { + super(); this._collection = collection; this.size = collection.length || collection.size; } diff --git a/src/Set.js b/src/Set.js index 7b2b3bd569..0e105b5976 100644 --- a/src/Set.js +++ b/src/Set.js @@ -1,4 +1,9 @@ -import { Collection, SetCollection, KeyedCollection } from './Collection'; +import { + Collection, + SetCollectionImpl, + KeyedCollection, + SetCollection, +} from './Collection'; import { isOrdered } from './predicates/isOrdered'; import { IS_SET_SYMBOL, isSet } from './predicates/isSet'; import { emptyMap } from './Map'; @@ -11,42 +16,40 @@ import { withMutations } from './methods/withMutations'; import { OrderedSet } from './OrderedSet'; -export class Set extends SetCollection { - // @pragma Construction +export const Set = value => + value === undefined || value === null + ? emptySet() + : isSet(value) && !isOrdered(value) + ? value + : emptySet().withMutations(set => { + const iter = SetCollection(value); + assertNotInfinite(iter.size); + iter.forEach(v => set.add(v)); + }); - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptySet() - : isSet(value) && !isOrdered(value) - ? value - : emptySet().withMutations(set => { - const iter = SetCollection(value); - assertNotInfinite(iter.size); - iter.forEach(v => set.add(v)); - }); - } +Set.of = function (/*...values*/) { + return Set(arguments); +}; - static of(/*...values*/) { - return this(arguments); - } +Set.fromKeys = value => Set(KeyedCollection(value).keySeq()); - static fromKeys(value) { - return this(KeyedCollection(value).keySeq()); - } +Set.intersect = sets => { + sets = Collection(sets).toArray(); + return sets.length + ? SetPrototype.intersect.apply(Set(sets.pop()), sets) + : emptySet(); +}; - static intersect(sets) { - sets = Collection(sets).toArray(); - return sets.length - ? SetPrototype.intersect.apply(Set(sets.pop()), sets) - : emptySet(); - } +Set.union = sets => { + const setArray = Collection(sets).toArray(); + return setArray.length + ? SetPrototype.union.apply(Set(setArray.pop()), setArray) + : emptySet(); +}; - static union(sets) { - sets = Collection(sets).toArray(); - return sets.length - ? SetPrototype.union.apply(Set(sets.pop()), sets) - : emptySet(); +export class SetImpl extends SetCollectionImpl { + create(value) { + return Set(value); } toString() { @@ -101,7 +104,7 @@ export class Set extends SetCollection { return this; } if (this.size === 0 && !this.__ownerID && iters.length === 1) { - return this.constructor(iters[0]); + return Set(iters[0]); } return this.withMutations(set => { for (let ii = 0; ii < iters.length; ii++) { @@ -191,7 +194,7 @@ export class Set extends SetCollection { Set.isSet = isSet; -const SetPrototype = Set.prototype; +const SetPrototype = SetImpl.prototype; SetPrototype[IS_SET_SYMBOL] = true; SetPrototype[DELETE] = SetPrototype.remove; SetPrototype.merge = SetPrototype.concat = SetPrototype.union; diff --git a/src/Stack.js b/src/Stack.js index 1ac627a71d..c930d78489 100644 --- a/src/Stack.js +++ b/src/Stack.js @@ -1,5 +1,5 @@ import { wholeSlice, resolveBegin, resolveEnd, wrapIndex } from './TrieUtils'; -import { IndexedCollection } from './Collection'; +import { IndexedCollection, IndexedCollectionImpl } from './Collection'; import { ArraySeq } from './Seq'; import { Iterator, iteratorValue, iteratorDone } from './Iterator'; import { IS_STACK_SYMBOL, isStack } from './predicates/isStack'; @@ -9,20 +9,20 @@ import { asMutable } from './methods/asMutable'; import { wasAltered } from './methods/wasAltered'; import { withMutations } from './methods/withMutations'; -export class Stack extends IndexedCollection { - // @pragma Construction +export const Stack = value => + value === undefined || value === null + ? emptyStack() + : isStack(value) + ? value + : emptyStack().pushAll(value); - constructor(value) { - // eslint-disable-next-line no-constructor-return - return value === undefined || value === null - ? emptyStack() - : isStack(value) - ? value - : emptyStack().pushAll(value); - } +Stack.of = function (/*...values*/) { + return Stack(arguments); +}; - static of(/*...values*/) { - return this(arguments); +export class StackImpl extends IndexedCollectionImpl { + create(value) { + return Stack(value); } toString() { @@ -122,7 +122,7 @@ export class Stack extends IndexedCollection { const resolvedEnd = resolveEnd(end, this.size); if (resolvedEnd !== this.size) { // super.slice(begin, end); - return IndexedCollection.prototype.slice.call(this, begin, end); + return IndexedCollectionImpl.prototype.slice.call(this, begin, end); } const newSize = this.size - resolvedBegin; let head = this._head; @@ -195,7 +195,7 @@ export class Stack extends IndexedCollection { Stack.isStack = isStack; -const StackPrototype = Stack.prototype; +const StackPrototype = StackImpl.prototype; StackPrototype[IS_STACK_SYMBOL] = true; StackPrototype.shift = StackPrototype.pop; StackPrototype.unshift = StackPrototype.push; diff --git a/src/methods/merge.js b/src/methods/merge.js index 5c28445087..d36256916f 100644 --- a/src/methods/merge.js +++ b/src/methods/merge.js @@ -29,7 +29,7 @@ function mergeIntoKeyedWith(collection, collections, merger) { !collection.__ownerID && iters.length === 1 ) { - return collection.constructor(iters[0]); + return collection.create(iters[0]); } return collection.withMutations(collection => { const mergeIntoCollection = merger