diff --git a/__tests__/Stretch.ts b/__tests__/Stretch.ts
new file mode 100644
index 000000000..615a409f4
--- /dev/null
+++ b/__tests__/Stretch.ts
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+///
+
+import * as jasmineCheck from 'jasmine-check';
+jasmineCheck.install();
+
+import { Stretch } from '../';
+
+describe('Stretch', () => {
+ it('fixed stretch', () => {
+ const v = Stretch(5, 3);
+ expect(v.size).toBe(3);
+ expect(v.first()).toBe(2);
+ expect(v.rest().toArray()).toEqual([5, 8]);
+ expect(v.last()).toBe(8);
+ expect(v.butLast().toArray()).toEqual([2, 5]);
+ expect(v.toArray()).toEqual([2, 5, 8]);
+ });
+
+ it('ends and front', () => {
+ const v = Stretch(5, 3, 4);
+ expect(v.size).toBe(3);
+ expect(v.first()).toBe(2);
+ expect(v.rest().toArray()).toEqual([5, 9]);
+ expect(v.last()).toBe(9);
+ expect(v.butLast().toArray()).toEqual([2, 5]);
+ expect(v.toArray()).toEqual([2, 5, 9]);
+ });
+
+ it('slices stretch', () => {
+ const v = Stretch(5, 3, 4);
+ const s = v.slice(0, 2);
+ expect(s.size).toBe(2);
+ expect(s.toArray()).toEqual([2, 5]);
+ });
+
+ it('one value', () => {
+ const v = Stretch(5, 3, 4);
+ const s = v.slice(0, 1);
+ expect(s.size).toBe(1);
+ expect(s.toArray()).toEqual([2]);
+ });
+
+ it('maps values', () => {
+ const r = Stretch(5, 3).map(v => v * v);
+ expect(r.toArray()).toEqual([4, 25, 64]);
+ });
+
+ it('reduces values', () => {
+ const v = Stretch(5, 3);
+ const r = v.reduce((a, b) => a + b, 0);
+ expect(r).toEqual(15);
+ });
+
+ it('can be float', () => {
+ const v = Stretch(3.5, 1.5, 2.5);
+ expect(v.size).toBe(3);
+ expect(v.toArray()).toEqual([2, 3.5, 6]);
+ });
+
+ it('can be negative', () => {
+ const v = Stretch(-5, -5, -6);
+ expect(v.size).toBe(3);
+ expect(v.toArray()).toEqual([0, -5, -11]);
+ });
+});
diff --git a/src/Immutable.js b/src/Immutable.js
index de672c8a7..336c93f1e 100644
--- a/src/Immutable.js
+++ b/src/Immutable.js
@@ -15,6 +15,7 @@ import { Set } from './Set';
import { Record } from './Record';
import { Range } from './Range';
import { Repeat } from './Repeat';
+import { Stretch } from './Stretch';
import { is } from './is';
import { fromJS } from './fromJS';
@@ -71,6 +72,7 @@ export default {
Record: Record,
Range: Range,
Repeat: Repeat,
+ Stretch: Stretch,
is: is,
fromJS: fromJS,
@@ -125,6 +127,7 @@ export {
Record,
Range,
Repeat,
+ Stretch,
is,
fromJS,
hash,
diff --git a/src/Stretch.js b/src/Stretch.js
new file mode 100644
index 000000000..b56ecb25a
--- /dev/null
+++ b/src/Stretch.js
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import { wholeSlice, resolveBegin, resolveEnd } from './TrieUtils';
+import { IndexedSeq } from './Seq';
+import invariant from './utils/invariant';
+import deepEqual from './utils/deepEqual';
+import { Iterator, iteratorValue, iteratorDone } from './Iterator';
+
+/**
+ * Returns a lazy seq of base - ends, base, base + front,
+ * if front is not provided it returns base + ends,
+ * `base` and `ends` both default to 1
+ */
+export class Stretch {
+ constructor(base, ends, front) {
+ if (!(this instanceof Stretch)) {
+ return new Stretch(base, ends, front);
+ }
+ invariant(
+ base !== Infinity || ends !== Infinity || front !== Infinity,
+ `Must be a finite number`
+ );
+ invariant(base !== undefined, `Base is required`);
+ ends = ends === undefined ? 1 : ends;
+ front = front === undefined ? ends : front;
+ this._base = base;
+ this._start = base - ends;
+ this._end = base + front;
+ this.size = 3;
+ /*when i tried extending IndexedSeq or ArraySeq and calling super
+ the tests __tests__/Stretch.ts returned [5, 8, undefined] instead of 2, 5, 8]
+ and [5, 9, undefined] instead of [2, 5, 9]*/
+ return new IndexedSeq([this._start, this._base, this._end]);
+ }
+ toString() {
+ return `[${this._start}, ${this._base}, ${this._end}]`;
+ }
+
+ get(index, notSetValue) {
+ return index === 0
+ ? this._start
+ : index === 1
+ ? this._base
+ : index === 2
+ ? this._end
+ : notSetValue;
+ }
+
+ includes(searchValue) {
+ return (
+ searchValue === this._start ||
+ searchValue === this._base ||
+ searchValue === this._end
+ );
+ }
+
+ slice(begin, end) {
+ if (wholeSlice(begin, end, this.size)) {
+ return this;
+ }
+ begin = resolveBegin(begin, this.size);
+ end = resolveEnd(end, this.size);
+ return end <= begin
+ ? new IndexedSeq([])
+ : end - begin === 1
+ ? new IndexedSeq([this.get(begin)])
+ : new IndexedSeq([this.get(begin), this.get(begin + 1)]);
+ }
+
+ indexOf(searchValue) {
+ return searchValue === this._start
+ ? 0
+ : searchValue === this._base
+ ? 1
+ : searchValue === this._end
+ ? 2
+ : -1;
+ }
+ /*we can't return indexOf for lastIndexOf because ends and front can
+be be different values*/
+ lastIndexOf(searchValue) {
+ return searchValue === this._end
+ ? 2
+ : searchValue === this._base
+ ? 1
+ : searchValue === this._start
+ ? 0
+ : -1;
+ }
+
+ __iterate(fn, reverse) {
+ const size = 3;
+ let value = reverse ? this._end : this._start;
+ let i = 0;
+ value += reverse ? -(this._end - this._base) : this._base - this._start;
+ if (fn(value, reverse ? size - ++i : i++, this) === false) {
+ return i;
+ }
+ value += reverse ? -(this._base - this._start) : this._end - this._base;
+ if (fn(value, reverse ? size - ++i : i++, this) === false) {
+ return i;
+ }
+ return i;
+ }
+
+ __iterator(type, reverse) {
+ const array = [this._start, this._base, this._end];
+ const size = 3;
+ let i = 0;
+ return new Iterator(() => {
+ if (i === size) {
+ return iteratorDone();
+ }
+ const ii = reverse ? size - ++i : i++;
+ return iteratorValue(type, ii, array[ii]);
+ });
+ }
+
+ reverse() {
+ return new Stretch(this._base, -this._ends, -this._front);
+ }
+
+ interpose(separator) {
+ return new IndexedSeq([
+ this._start,
+ separator,
+ this._base,
+ separator,
+ this._end,
+ ]);
+ }
+
+ rest() {
+ return new IndexedSeq([this._base, this._end]);
+ }
+
+ butLast() {
+ return new IndexedSeq([this._start, this._base]);
+ }
+
+ equals(other) {
+ return other instanceof Stretch
+ ? this._start === other._start &&
+ this._end === other._end &&
+ this._base === other._base
+ : deepEqual(this, other);
+ }
+}
diff --git a/type-definitions/Immutable.d.ts b/type-definitions/Immutable.d.ts
index baae41d01..928bf91da 100644
--- a/type-definitions/Immutable.d.ts
+++ b/type-definitions/Immutable.d.ts
@@ -2203,6 +2203,23 @@ declare module Immutable {
*/
export function Repeat(value: T, times?: number): Seq.Indexed;
+ /**
+ * Returns a Seq.Indexed with `base` - `ends`, `base`, and `base` + `front`, if
+ * front is not provided it returns `base` + `ends`, `base` is required and `ends`
+ * defaults to 1
+ *
+ * Note: `Stretch` is a factory function and not a class, and does not use the
+ * `new` keyword during construction.
+ *
+ * ```js
+ * const { Stretch } = require('immutable')
+ * Stretch(10, 5) // [5, 10, 15]
+ * Stretch(10, 5, 3) // [5, 10, 13]
+ * Stretch(5) // [4, 5, 6]
+ * ```
+ */
+ export function Stretch(base: number, ends?: number, front?: number): Seq.Indexed;
+
/**
* A record is similar to a JS object, but enforces a specific set of allowed
diff --git a/type-definitions/immutable.js.flow b/type-definitions/immutable.js.flow
index 2b37f4143..64e76dca9 100644
--- a/type-definitions/immutable.js.flow
+++ b/type-definitions/immutable.js.flow
@@ -1380,6 +1380,7 @@ declare class Stack<+T> extends IndexedCollection {
declare function Range(start?: number, end?: number, step?: number): IndexedSeq;
declare function Repeat(value: T, times?: number): IndexedSeq;
+declare function Stretch(base: number, ends?: number, front?: number): IndexedSeq;
// The type of a Record factory function.
type RecordFactory = Class>;
@@ -1581,6 +1582,7 @@ export {
OrderedSet,
Range,
Repeat,
+ Stretch
Record,
Set,
Stack,
@@ -1624,6 +1626,7 @@ export default {
OrderedSet,
Range,
Repeat,
+ Stretch,
Record,
Set,
Stack,
diff --git a/type-definitions/tests/immutable-flow.js b/type-definitions/tests/immutable-flow.js
index 0bc7289b0..33fc14b31 100644
--- a/type-definitions/tests/immutable-flow.js
+++ b/type-definitions/tests/immutable-flow.js
@@ -18,6 +18,7 @@ import Immutable, {
Seq,
Range,
Repeat,
+ Stretch,
Record,
OrderedMap,
OrderedSet,
@@ -64,6 +65,7 @@ const ImmutableSet = Immutable.Set
const ImmutableKeyedCollection: KeyedCollection<*, *> = Immutable.Collection.Keyed()
const ImmutableRange = Immutable.Range
const ImmutableRepeat = Immutable.Repeat
+const ImmutableStretch = Immutable.Stretch
const ImmutableIndexedSeq: IndexedSeq<*> = Immutable.Seq.Indexed()
const Immutable2List = Immutable2.List
@@ -73,6 +75,7 @@ const Immutable2Set = Immutable2.Set
const Immutable2KeyedCollection: Immutable2.KeyedCollection<*, *> = Immutable2.Collection.Keyed()
const Immutable2Range = Immutable2.Range
const Immutable2Repeat = Immutable2.Repeat
+const Immutable2Stretch = Immutable2.Stretch
const Immutable2IndexedSeq: Immutable2.IndexedSeq<*> = Immutable2.Seq.Indexed()
var defaultExport: List<*> = Immutable.List();
@@ -884,17 +887,20 @@ numberStack = Stack([1]).flatMap((value, index, iter) => ['a'])
numberStack = Stack([1]).flatten()
numberStack = Stack(['a']).flatten()
-/* Range & Repeat */
+/* Range & Repeat & Stretch */
// `{}` provide namespaces
{ const numberSequence: IndexedSeq = Range(0, 0, 0) }
{ const numberSequence: IndexedSeq = Repeat(1, 5) }
+{ const numberSequence: IndexedSeq = Stretch(5, 3) }
{ const stringSequence: IndexedSeq = Repeat('a', 5) }
// $ExpectError
{ const stringSequence: IndexedSeq = Repeat(0, 1) }
// $ExpectError
{ const stringSequence: IndexedSeq = Range(0, 0, 0) }
+// $ExpectError
+{ const stringSequence: IndexedSeq = Stretch('a', 3) }
/* Seq */
diff --git a/type-definitions/ts-tests/exports.ts b/type-definitions/ts-tests/exports.ts
index 8da25cda0..4190570a1 100644
--- a/type-definitions/ts-tests/exports.ts
+++ b/type-definitions/ts-tests/exports.ts
@@ -15,6 +15,7 @@ import {
OrderedSet,
Range,
Repeat,
+ Stretch,
Seq,
Set,
Stack,
@@ -28,6 +29,7 @@ OrderedSet; // $ExpectType typeof OrderedSet
// TODO: Turn on once https://github.com/Microsoft/dtslint/issues/19 is resolved.
Range; // $ ExpectType (start?: number | undefined, end?: number | undefined, step?: number | undefined) => Indexed
Repeat; // $ExpectType (value: T, times?: number | undefined) => Indexed
+Stretch; // $ExpectType (base: number, ends?: number | undefined, front?: number | undefined) => Indexed
Seq; // $ExpectType typeof Seq
Set; // $ExpectType typeof Set
Stack; // $ExpectType typeof Stack
@@ -43,6 +45,7 @@ Immutable.OrderedSet; // $ExpectType typeof OrderedSet
// TODO: Turn on once https://github.com/Microsoft/dtslint/issues/19 is resolved.
Immutable.Range; // $ ExpectType (start?: number | undefined, end?: number | undefined, step?: number | undefined) => Indexed
Immutable.Repeat; // $ExpectType (value: T, times?: number | undefined) => Indexed
+Immutable.Stretch; // $ExpectType (base: number, ends?: number | undefined, front?: number | undefined) => Indexed
Immutable.Seq; // $ExpectType typeof Seq
Immutable.Set; // $ExpectType typeof Set
Immutable.Stack; // $ExpectType typeof Stack
diff --git a/type-definitions/ts-tests/stretch.ts b/type-definitions/ts-tests/stretch.ts
new file mode 100644
index 000000000..0c495b07e
--- /dev/null
+++ b/type-definitions/ts-tests/stretch.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Stretch } from '../../';
+
+{ // #constructor
+
+ // $ExpectType Indexed
+ Stretch(5, 3);
+
+ // $ExpectError
+ Stretch('a', 3);
+}