Last active
December 4, 2022 14:43
-
-
Save jonathantneal/e43d848244c9e03c801e046f9918f711 to your computer and use it in GitHub Desktop.
Iterator.prototype helpers polyfill — https://tc39.es/proposal-iterator-helpers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Iterator<T, TReturn = any, TNext = undefined> { | |
next(): { | |
done: boolean | |
value: T | |
} | |
/** Returns an iterator of the elements with the given mapping function applied. */ | |
map<TT>( | |
/** A mapping function to call on every element of the iterator. */ | |
callback: (value: T) => TT | |
): Iterator<TT, TReturn, TNext> | |
/** Returns an iterator of the elements with elements that meet the condition specified in the given filtering function. */ | |
filter<TT extends T>( | |
/** A filtering function to call on every element of the iterator. */ | |
callback: (value: T) => value is TT | |
): Iterator<TT, TReturn, TNext> | |
/** Returns an iterator that produces, at most, the specified number of elements, or the given number of elements produced by the underlying iterator. */ | |
take<TT extends T>( | |
/** A number of elements to take. */ | |
limit: number | |
): Iterator<TT, TReturn, TNext> | |
/** Returns an iterator that skips the given number of elements produced by the underlying iterator. */ | |
drop<TT extends T>( | |
/** A number of elements to drop. */ | |
limit: number | |
): Iterator<TT, TReturn, TNext> | |
/** Returns an iterator where each value produced by the underlying iterator is paired with a counter. */ | |
indexed(): Iterator<[ number, T ], TReturn, TNext> | |
/** Returns the accumulated result of the elements produced by the iterator. */ | |
reduce<TT>(callback: (accumulator: TT, currentValue: T) => TT): TT | |
reduce<TT>(callback: (accumulator: TT, currentValue: T) => TT, initialValue: TT): TT | |
/** Returns an array of the iterable elements. */ | |
toArray(): T[] | |
/** Calls the given callback on each element of the iterator. */ | |
forEach( | |
/** A function to call on every element of the iterator. */ | |
callback: (value: T) => void | |
): void | |
/** Returns whether the given callback function returns true for any element of the iterator. */ | |
some(callback: (value: T) => unknown): boolean | |
/** Returns whether the given callback function returns true for every element of the iterator. */ | |
every(callback: (value: T) => unknown): boolean | |
} | |
type iterator = SymbolConstructor['iterator'] | |
interface IteratorConstructor { | |
new (iteratorLike: any) | |
from<T>(object: T): iterator extends keyof T ? ReturnType<T[iterator]> : Iterator<ReturnType<T['next']>['value']> | |
} | |
global { | |
var Iterator: IteratorConstructor | |
} | |
export {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Iterator Helpers Polyfill v0.1.0 | CC0-1.0 */ | |
/* 1729 bytes minified, 724 bytes gzipped. */ | |
{ | |
/** Prototype of the Iterator. */ | |
let { iterator } = Symbol | |
let Iterator = globalThis.Iterator = function Iterator() {} | |
let prototypeOfIterator = Iterator.prototype = [].keys().__proto__.__proto__ | |
let map = new WeakMap | |
/** Validates that a callback is a function and otherwise throws an error. */ | |
let validateCallback = /** @type {(callback: any, name: string) => void} */ (callback, name) => { | |
if (typeof callback != 'function') { | |
throw new TypeError('Iterator.prototype.' + name + ' callback must be a function') | |
} | |
} | |
let validateThis = /** @type {(object: unknown) => unknown} */ (object) => { | |
if (!map.has(object)) throw new TypeError('Illegal invocation.') | |
return map.get(object) | |
} | |
let prototype = Object.getOwnPropertyDescriptors({ | |
constructor: Iterator, | |
next() { | |
return validateThis(this).next(...arguments) | |
}, | |
return() { | |
validateThis(this).return(...arguments) | |
}, | |
throw() { | |
validateThis(this).throw(...arguments) | |
}, | |
/** Returns an iterator of the elements with the map function applied. | |
@type {( | |
this: Iterator<unknown, unknown, unknown>, | |
callback: (value: unknown) => unknown | |
) => Iterator<unknown, unknown, unknown>}} */ | |
map(callback) { | |
validateCallback(callback, 'map') | |
return function*(it, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) { | |
while ({ done, value } = it.next(), !done) { | |
yield callback(value) | |
} | |
}(this) | |
}, | |
/** Returns an iterator of the elements with elements that meet the condition specified in the given filtering function. | |
@type {( | |
this: Iterator<unknown, unknown, unknown>, | |
callback: (value: unknown) => boolean | |
) => Iterator<unknown, unknown, unknown>} */ | |
filter(callback) { | |
validateCallback(callback, 'filter') | |
return function*(it, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) { | |
while ({ done, value } = it.next(), !done) { | |
if (callback(value)) yield value | |
} | |
}(this) | |
}, | |
/** Returns an iterator that produces, at most, the specified number of elements, or the given number of elements produced by the underlying iterator. | |
@type {( | |
this: Iterator<unknown, unknown, unknown>, | |
limit: number | |
) => Iterator<unknown, unknown, unknown>}} */ | |
take(limit) { | |
limit = Number(limit) || 0 | |
return function*(it, thisLimit = limit, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) { | |
while ({ done, value } = it.next(), !done) { | |
yield value | |
if (!--thisLimit) break | |
} | |
}(this) | |
}, | |
/** Returns an iterator that skips the given number of elements produced by the underlying iterator. | |
@type {( | |
this: Iterator<unknown, unknown, unknown>, | |
limit: number | |
) => Iterator<unknown, unknown, unknown>} */ | |
drop(limit) { | |
limit = Number(limit) || 0 | |
return function*(it, thisLimit = limit, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) { | |
while ({ done, value } = it.next(), !done) { | |
if (--thisLimit >= 0) continue | |
yield value | |
} | |
}(this) | |
}, | |
/** Returns an iterator where each value produced by the underlying iterator is paired with a counter. | |
@type {( | |
this: Iterator<unknown, unknown, unknown> | |
) => Iterator<[ number, unknown ], unknown, unknown>} */ | |
indexed() { | |
return function*(it, /** @type {number} */ index, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) { | |
index = -1 | |
while ({ done, value } = it.next(), !done) { | |
yield [ ++index, value ] | |
} | |
}(this) | |
}, | |
/** Returns an iterator where each value produced by the underlying iterator is paired with a counter. | |
@type {( | |
this: Iterator<unknown, unknown, unknown>, | |
callback: (accumulator: unknown, currentValue: unknown) => unknown, initialValue: unknown | |
) => unknown} */ | |
reduce(callback) { | |
validateCallback(callback, 'reduce') | |
let value = 1 in arguments ? arguments[1] : this.next().value, accumulator = value, done | |
while ({ done, value } = this.next(), !done) { | |
accumulator = callback(accumulator, value) | |
} | |
return accumulator | |
}, | |
/** Returns an array of the iterable elements. | |
@type {( | |
this: Iterator | |
) => unknown[]} */ | |
toArray() { | |
return [ ...this ] | |
}, | |
/** Calls the given callback on each element of the iterator. | |
@type {( | |
this: Iterator, | |
callback: (value: unknown) => void | |
) => void} */ | |
forEach(callback) { | |
validateCallback(callback, 'forEach') | |
let done, value | |
while ({ done, value } = this.next(), !done) { | |
callback(value) | |
} | |
}, | |
/** Returns whether the given callback function returns true for any element of the iterator. | |
@type {( | |
this: Iterator, | |
callback: (value: unknown) => boolean | |
) => boolean} */ | |
some(callback) { | |
validateCallback(callback, 'every') | |
let every = true, done, value | |
while ({ done, value } = this.next(), !done) { | |
every = every && callback(value) | |
} | |
return Boolean(every) | |
}, | |
/** Returns whether the given callback function returns true for every element of the iterator. | |
@type {( | |
this: Iterator, | |
callback: (value: unknown) => boolean | |
) => boolean} */ | |
every(callback) { | |
validateCallback(callback, 'every') | |
let every = true, done, value | |
while ({ done, value } = this.next(), !done) { | |
every = every && callback(value) | |
} | |
return Boolean(every) | |
}, | |
}) | |
let key | |
for (key in prototype) { | |
if (key in prototypeOfIterator) delete prototype[key] | |
else delete prototype[key].enumerable | |
} | |
Object.defineProperties(prototypeOfIterator, prototype) | |
Object.defineProperties(Iterator, { | |
from: { | |
configurable: true, | |
value: { | |
from(object) { | |
/** @type {Iterator<unknown, unknown, unknown>} */ | |
let returnValue | |
object = Object(object) | |
if (typeof object[iterator] == 'function') { | |
object = object[iterator]() | |
if (object instanceof Iterator) { | |
return object | |
} | |
} | |
returnValue = Object.create(prototypeOfIterator) | |
map.set(returnValue, object) | |
return returnValue | |
}, | |
}.from | |
} | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{let e,{iterator:t}=Symbol,r=globalThis.Iterator=function(){},n=r.prototype=[].keys().__proto__.__proto__,o=new WeakMap,i=(e,t)=>{if("function"!=typeof e)throw new TypeError("Iterator.prototype."+t+" callback must be a function")},u=e=>{if(!o.has(e))throw new TypeError("Illegal invocation.") | |
return o.get(e)},l=Object.getOwnPropertyDescriptors({constructor:r,next(){return u(this).next(...arguments)},return(){u(this).return(...arguments)},throw(){u(this).throw(...arguments)},map(e){return i(e,"map"),function*(t,r,n){for(;({done:r,value:n}=t.next()),!r;)yield e(n)}(this)},filter(e){return i(e,"filter"),function*(t,r,n){for(;({done:r,value:n}=t.next()),!r;)e(n)&&(yield n)}(this)},take(e){return e=Number(e)||0,function*(t,r=e,n,o){for(;({done:n,value:o}=t.next()),!n&&(yield o,--r););}(this)},drop(e){return e=Number(e)||0,function*(t,r=e,n,o){for(;({done:n,value:o}=t.next()),!n;)--r>=0||(yield o)}(this)},indexed(){return function*(e,t,r,n){for(t=-1;({done:r,value:n}=e.next()),!r;)yield[++t,n]}(this)},reduce(e){i(e,"reduce") | |
let t,r=1 in arguments?arguments[1]:this.next().value,n=r | |
for(;({done:t,value:r}=this.next()),!t;)n=e(n,r) | |
return n},toArray(){return[...this]},forEach(e){let t,r | |
for(i(e,"forEach");({done:t,value:r}=this.next()),!t;)e(r)},some(e){i(e,"every") | |
let t,r,n=!0 | |
for(;({done:t,value:r}=this.next()),!t;)n=n&&e(r) | |
return Boolean(n)},every(e){i(e,"every") | |
let t,r,n=!0 | |
for(;({done:t,value:r}=this.next()),!t;)n=n&&e(r) | |
return Boolean(n)}}) | |
for(e in l)e in n?delete l[e]:delete l[e].enumerable | |
Object.defineProperties(n,l),Object.defineProperties(r,{from:{configurable:!0,value:{from(e){let i | |
return"function"==typeof(e=Object(e))[t]&&(e=e[t]())instanceof r?e:(i=Object.create(n),o.set(i,e),i)}}.from}})} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment