diff --git a/src/Map.ts b/src/Map.ts index 4cdeff2..cf6a3d0 100644 --- a/src/Map.ts +++ b/src/Map.ts @@ -1,15 +1,131 @@ -import { ArrayLike } from './interfaces'; -import { hasClass } from './support/decorators'; -import global from './global'; import { forOf, Iterable, IterableIterator, ShimIterator } from './iterator'; +import global from './global'; import { is as objectIs } from './object'; +import has from './support/has'; import './Symbol'; -export namespace Shim { +export interface Map { + /** + * Deletes all keys and their associated values. + */ + clear(): void; + + /** + * Deletes a given key and its associated value. + * + * @param key The key to delete + * @return true if the key exists, false if it does not + */ + delete(key: K): boolean; + + /** + * Returns an iterator that yields each key/value pair as an array. + * + * @return An iterator for each key/value pair in the instance. + */ + entries(): IterableIterator<[K, V]>; + + /** + * Executes a given function for each map entry. The function + * is invoked with three arguments: the element value, the + * element key, and the associated Map instance. + * + * @param callbackfn The function to execute for each map entry, + * @param thisArg The value to use for `this` for each execution of the calback + */ + forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void; + + /** + * Returns the value associated with a given key. + * + * @param key The key to look up + * @return The value if one exists or undefined + */ + get(key: K): V | undefined; + + /** + * Returns an iterator that yields each key in the map. + * + * @return An iterator containing the instance's keys. + */ + keys(): IterableIterator; + + /** + * Checks for the presence of a given key. + * + * @param key The key to check for + * @return true if the key exists, false if it does not + */ + has(key: K): boolean; + + /** + * Sets the value associated with a given key. + * + * @param key The key to define a value to + * @param value The value to assign + * @return The Map instance + */ + set(key: K, value: V): this; + + /** + * Returns the number of key / value pairs in the Map. + */ + readonly size: number; + + /** + * Returns an iterator that yields each value in the map. + * + * @return An iterator containing the instance's values. + */ + values(): IterableIterator; + + /** Returns an iterable of entries in the map. */ + [Symbol.iterator](): IterableIterator<[K, V]>; + + readonly [Symbol.toStringTag]: string; +} + +export interface MapConstructor { /** - * An implementation analogous to the Map specification in ES2015. + * Creates a new Map + * + * @constructor */ - export class Map { + new (): Map; + + /** + * Creates a new Map + * + * @constructor + * + * @param iterator + * Array or iterator containing two-item tuples used to initially populate the map. + * The first item in each tuple corresponds to the key of the map entry. + * The second item corresponds to the value of the map entry. + */ + new (iterator?: [K, V][]): Map; + + /** + * Creates a new Map + * + * @constructor + * + * @param iterator + * Array or iterator containing two-item tuples used to initially populate the map. + * The first item in each tuple corresponds to the key of the map entry. + * The second item corresponds to the value of the map entry. + */ + new (iterator: Iterable<[K, V]>): Map; + + readonly prototype: Map; + + readonly [Symbol.species]: MapConstructor; +} + +export let Map: MapConstructor = global.Map; + +if (!has('es6-map')) { + Map = class Map { protected readonly _keys: K[] = []; protected readonly _values: V[] = []; @@ -26,16 +142,8 @@ export namespace Shim { return -1; } - /** - * Creates a new Map - * - * @constructor - * - * @param iterator - * Array or iterator containing two-item tuples used to initially populate the map. - * The first item in each tuple corresponds to the key of the map entry. - * The second item corresponds to the value of the map entry. - */ + static [Symbol.species] = Map; + constructor(iterable?: ArrayLike<[K, V]> | Iterable<[K, V]>) { if (iterable) { forOf(iterable, (value: [K, V]) => { @@ -44,28 +152,14 @@ export namespace Shim { } } - /** - * Returns the number of key / value pairs in the Map. - * - * @return the number of key / value pairs in the Map - */ get size(): number { return this._keys.length; } - /** - * Deletes all keys and their associated values. - */ clear(): void { this._keys.length = this._values.length = 0; } - /** - * Deletes a given key and its associated value. - * - * @param key The key to delete - * @return true if the key exists, false if it does not - */ delete(key: K): boolean { const index = this._indexOfKey(this._keys, key); if (index < 0) { @@ -76,27 +170,14 @@ export namespace Shim { return true; } - /** - * Returns an iterator that yields each key/value pair as an array. - * - * @return An iterator for each key/value pair in the instance. - */ entries(): IterableIterator<[K, V]> { const values = this._keys.map((key: K, i: number): [K, V] => { return [ key, this._values[i] ]; }); - return new ShimIterator<[K, V]>(values); + return new ShimIterator(values); } - /** - * Executes a given function for each map entry. The function - * is invoked with three arguments: the element value, the - * element key, and the associated Map instance. - * - * @param callback The function to execute for each map entry, - * @param context The value to use for `this` for each execution of the calback - */ forEach(callback: (value: V, key: K, mapInstance: Map) => any, context?: {}) { const keys = this._keys; const values = this._values; @@ -105,43 +186,19 @@ export namespace Shim { } } - /** - * Returns the value associated with a given key. - * - * @param key The key to look up - * @return The value if one exists or undefined - */ get(key: K): V | undefined { const index = this._indexOfKey(this._keys, key); return index < 0 ? undefined : this._values[index]; } - /** - * Checks for the presence of a given key. - * - * @param key The key to check for - * @return true if the key exists, false if it does not - */ has(key: K): boolean { return this._indexOfKey(this._keys, key) > -1; } - /** - * Returns an iterator that yields each key in the map. - * - * @return An iterator containing the instance's keys. - */ keys(): IterableIterator { - return new ShimIterator(this._keys); + return new ShimIterator(this._keys); } - /** - * Sets the value associated with a given key. - * - * @param key The key to define a value to - * @param value The value to assign - * @return The Map instance - */ set(key: K, value: V): Map { let index = this._indexOfKey(this._keys, key); index = index < 0 ? this._keys.length : index; @@ -150,50 +207,16 @@ export namespace Shim { return this; } - /** - * Returns an iterator that yields each value in the map. - * - * @return An iterator containing the instance's values. - */ values(): IterableIterator { - return new ShimIterator(this._values); + return new ShimIterator(this._values); } [Symbol.iterator](): IterableIterator<[K, V]> { return this.entries(); } - [Symbol.toStringTag] = 'Map'; - } + [Symbol.toStringTag]: 'Map' = 'Map'; + }; } -@hasClass('es6-map', global.Map, Shim.Map) -export default class Map { - /* istanbul ignore next */ - constructor(iterable?: ArrayLike<[K, V]> | Iterable<[K, V]>) { }; - - /* istanbul ignore next */ - get size(): number { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - clear(): void { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - delete(key: K): boolean { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - entries(): IterableIterator<[K, V]> { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - forEach(callback: (value: V, key: K, mapInstance: Map) => any, context?: {}): void { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - get(key: K): V | undefined { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - has(key: K): boolean { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - keys(): IterableIterator { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - set(key: K, value: V): Map { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - values(): IterableIterator { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - [Symbol.iterator](): IterableIterator<[K, V]> { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - [Symbol.toStringTag] = 'Map'; -} +export default Map; diff --git a/src/Observable.ts b/src/Observable.ts index 7ae2690..51dc576 100644 --- a/src/Observable.ts +++ b/src/Observable.ts @@ -1,29 +1,83 @@ -import { Iterable, forOf, isIterable, isArrayLike } from './iterator'; -import { hasClass } from './support/decorators'; import global from './global'; +import { Iterable, forOf, isIterable, isArrayLike } from './iterator'; +import has from './support/has'; import './Symbol'; -/** - * Handles an individual subscription to an Observable. - */ -export interface Subscription { +export interface Observable extends ObservableObject { /** - * Whether or not the subscription is closed. Closed subscriptions will not emit values. + * Registers handlers for handling emitted values, error and completions from the observable, and + * executes the observable's subscriber function, which will take action to set up the underlying data stream. + * + * @param observer The observer object that will handle events + * + * @return A Subscription object that can be used to manage the subscription. */ - closed: boolean; + subscribe(observer: Observer): Subscription; /** - * A function to call to close the subscription. Calling this will call any associated tear down methods. + * Registers handlers for handling emitted values, error and completions from the observable, and + * executes the observable's subscriber function, which will take action to set up the underlying data stream. + * + * @param onNext A function to handle an emitted value. Value is passed in as the first parameter to the function. + * @param onError A function to handle errors that occur during onNext, or during subscription. + * @param onComplete A function that gets called when the subscription is complete, and will not send any more values. This function will also get called if an error occurs and onError is not defined. + * + * @return {Subscription} A Subscription object that can be used to manage the subscription. */ - unsubscribe: (() => void); + subscribe(onNext: (value: T) => any, onError?: (error: any) => any, onComplete?: (completeValue?: any) => void): Subscription; + + [Symbol.observable](): this; +} + +export interface ObservableConstructor { + /** + * Create a new observerable with a subscriber function. The subscriber function will get called with a + * SubscriptionObserver parameter for controlling the subscription. I a function is returned, it will be + * run when the subscription is complete. + * + * @param subscriber The subscription function to be called when observers are subscribed + * + * @example + * ```ts + * const source = new Observer((observer) => { + * observer.next(1); + * observer.next(2); + * observer.next(3); + * }); + * ```ts + */ + new (subscriber: Subscriber): Observable; + + /** + * Create an Observable from another object. If the object is in itself Observable, the object will be returned. + * Otherwise, the value will be wrapped in an Observable. If the object is iterable, an Observable will be created + * that emits each item of the iterable. + * + * @param item The item to be turned into an Observable + * @return An observable for the item you passed in + */ + from(item: Iterable | ArrayLike | ObservableObject): Observable; + + /** + * Create an Observable from a list of values. + * + * @param items The values to be emitted + * @return An Observable that will emit the specified values + * + * @example + * ```ts + * let source = Observable.of(1, 2, 3); + * // will emit three separate values, 1, 2, and 3. + * ``` + */ + of(...items: T[]): Observable; } /** - * Describes an object that can be subscribed to + * An object that implements a Symbol.observerable method. */ -export interface Subscribable { - subscribe(observer: Observer): Subscription; - subscribe(onNext: (value: T) => any, onError?: (error: any) => any, onComplete?: (completeValue?: any) => void): Subscription; +export interface ObservableObject { + [Symbol.observable]: () => any; } /** @@ -33,7 +87,7 @@ export interface Observer { /** * Called to handle a single emitted event. * - * @param {T} value The value that was emitted. + * @param value The value that was emitted. */ next?(value: T): any; @@ -46,18 +100,45 @@ export interface Observer { /** * An optional method to be called if an error occurs during subscription or handling. * - * @param errorValue The error + * @param errorValue The error */ error?(errorValue: any): any; /** * An optional method to be called when the subscription is completed (unless an error occurred and the error method was specified) * - * @param completeValue The value passed to the completion method. + * @param completeValue The value passed to the completion method. */ complete?(completeValue?: any): void; } +/** + * Describes an object that can be subscribed to + */ +export interface Subscribable { + subscribe(observer: Observer): Subscription; + subscribe(onNext: (value: T) => any, onError?: (error: any) => any, onComplete?: (completeValue?: any) => void): Subscription; +} + +export interface Subscriber { + (observer: SubscriptionObserver): (() => void) | void | { unsubscribe: () => void }; +} + +/** + * Handles an individual subscription to an Observable. + */ +export interface Subscription { + /** + * Whether or not the subscription is closed. Closed subscriptions will not emit values. + */ + closed: boolean; + + /** + * A function to call to close the subscription. Calling this will call any associated tear down methods. + */ + unsubscribe: (() => void); +} + /** * An object used to control a single subscription and an observer. */ @@ -70,14 +151,14 @@ export interface SubscriptionObserver { /** * Emit an event to the observer. * - * @param value The value to be emitted. + * @param value The value to be emitted. */ next(value: T): any; /** * Report an error. The subscription will be closed after an error has occurred. * - * @param errorValue The error to be reported. + * @param errorValue The error to be reported. */ error(errorValue: any): any; @@ -85,36 +166,19 @@ export interface SubscriptionObserver { * Report completion of the subscription. The subscription will be closed, and no new values will be emitted, * after completion. * - * @param completeValue? A value to pass to the completion handler. + * @param completeValue A value to pass to the completion handler. */ complete(completeValue?: any): void; } -export interface Subscriber { - (observer: SubscriptionObserver): (() => void) | void | { unsubscribe: () => void }; -} - -/** - * An object that implements a Symbol.observerable method. - */ -export interface ObservableObject { - [Symbol.observable]: () => any; -} - -namespace Shim { - /* - * Decorator to mark a single method/property as non-enumerable. ES spec requires pretty much every - * method or property in Subscription, Observable, and SubscriptionObserver to be non-enumerable. - */ - function nonEnumerable(target: any, key: string | symbol, descriptor: PropertyDescriptor) { - descriptor.enumerable = false; - } +export let Observable: ObservableConstructor = global.Observable; +if (!has('es-observable')) { /* * Create a subscription observer for a given observer, and return the subscription. The "logic" for Observerables * is in here! */ - function startSubscription(executor: Subscriber, observer: Observer): Subscription { + const startSubscription = function startSubscription(executor: Subscriber, observer: Observer): Subscription { let closed = false; let cleanUp: () => void | undefined; @@ -298,219 +362,140 @@ namespace Shim { }); // create the SubscriptionObserver and kick things off - start(> Object.create(prototype)); + start(Object.create(prototype)); // the ONLY way to control the SubscriptionObserver is with the subscription or from a subscriber return subscription; - } - - export class ShimObservable implements Observable { - private _executor: Subscriber; - - @nonEnumerable - [Symbol.observable](): Observable { - return this; - } - - constructor(subscriber: Subscriber) { - if (typeof subscriber !== 'function') { - throw new TypeError('subscriber is not a function'); - } + }; - this._executor = subscriber; + Observable = (function () { + function nonEnumerable(target: any, key: string | symbol, descriptor: PropertyDescriptor) { + descriptor.enumerable = false; } - subscribe(onNext: (value: T) => any, onError?: (error: any) => any, onComplete?: (value: any) => void): Subscription; - subscribe(observer: Observer): Subscription; - subscribe(observerOrNext: any, onError?: (error: any) => any, onComplete?: (value: any) => void): Subscription; - @nonEnumerable - subscribe(observerOrNext: any, ...listeners: any[]) { - const [ onError, onComplete ] = [ ...listeners ]; + class Observable { + private _executor: Subscriber; - if (!observerOrNext || typeof observerOrNext === 'number' || typeof observerOrNext === 'string' || typeof observerOrNext === 'boolean') { - throw new TypeError('parameter must be a function or an observer'); + @nonEnumerable + [Symbol.observable](): this { + return this; } - let observer: Observer; - - if (typeof observerOrNext === 'function') { - observer = { - next: observerOrNext - }; - - if (typeof onError === 'function') { - observer.error = onError; + constructor(subscriber: Subscriber) { + if (typeof subscriber !== 'function') { + throw new TypeError('subscriber is not a function'); } - if (typeof onComplete === 'function') { - observer.complete = onComplete; - } + this._executor = subscriber; } - else { - observer = observerOrNext; - } - - return startSubscription(this._executor, observer); - } - @nonEnumerable - static of(...items: U[]): ShimObservable { - let constructor: any; - - if (typeof this !== 'function') { - constructor = ShimObservable; - } - else { - constructor = this; - } - - return new constructor((observer: SubscriptionObserver) => { - forOf(items, (o: any) => { - observer.next(o); - }); - observer.complete(); - }); - } + @nonEnumerable + subscribe(observerOrNext: any, ...listeners: any[]) { + const [ onError, onComplete ] = [ ...listeners ]; - @nonEnumerable - static from(item: Iterable | ArrayLike | Observable): ShimObservable { - if (item === null || item === undefined) { - throw new TypeError('item cannot be null or undefined'); - } + if (!observerOrNext || typeof observerOrNext === 'number' || typeof observerOrNext === 'string' || typeof observerOrNext === 'boolean') { + throw new TypeError('parameter must be a function or an observer'); + } - let constructor: any; + let observer: Observer; - if (typeof this !== 'function') { - constructor = ShimObservable; - } - else { - constructor = this; - } + if (typeof observerOrNext === 'function') { + observer = { + next: observerOrNext + }; - const observableSymbol = (> item)[ Symbol.observable ]; + if (typeof onError === 'function') { + observer.error = onError; + } - if (observableSymbol !== undefined) { - if (typeof observableSymbol !== 'function') { - throw new TypeError('Symbol.observable must be a function'); + if (typeof onComplete === 'function') { + observer.complete = onComplete; + } + } + else { + observer = observerOrNext; } - const result: any = observableSymbol.call(item); + return startSubscription(this._executor, observer); + } - if (result === undefined || result === null || typeof result === 'number' || typeof result === 'boolean' || typeof result === 'string') { - throw new TypeError('Return value of Symbol.observable must be object'); - } + @nonEnumerable + static of(...items: U[]): Observable { + let constructor: typeof Observable; - if (result.constructor && result.constructor === this || result instanceof ShimObservable) { - return result; - } - else if (result.subscribe) { - return new constructor(result.subscribe); + if (typeof this !== 'function') { + constructor = Observable; } else { - if (constructor.of) { - return constructor.of(result); - } - else { - return ShimObservable.of(result); - } + constructor = this; } - } - else if (isIterable(item) || isArrayLike(item)) { + return new constructor((observer: SubscriptionObserver) => { - forOf(item, (o: any) => { + forOf(items, (o: any) => { observer.next(o); }); observer.complete(); }); } - else { - throw new TypeError('Parameter is neither Observable nor Iterable'); - } - } - } -} -@hasClass('es-observable', global.Observable, Shim.ShimObservable) -export default class Observable implements ObservableObject { + @nonEnumerable + static from(item: Iterable | ArrayLike | Observable): Observable { + if (item === null || item === undefined) { + throw new TypeError('item cannot be null or undefined'); + } - /* istanbul ignore next */ - /** - * Create a new observerable with a subscriber function. The subscriber function will get called with a - * SubscriptionObserver parameter for controlling the subscription. I a function is returned, it will be - * run when the subscription is complete. - * - * @param {Subscriber} subscriber The subscription function to be called when observers are subscribed - * - * @example - * const source = new Observer((observer) => { - * observer.next(1); - * observer.next(2); - * observer.next(3); - * }); - */ - constructor(subscriber: Subscriber) { - } + let constructor: typeof Observable; - /** - * Registers handlers for handling emitted values, error and completions from the observable, and - * executes the observable's subscriber function, which will take action to set up the underlying data stream. - * - * @param {Observer} observer The observer object that will handle events - * - * @return {Subscription} A Subscription object that can be used to manage the subscription. - */ - subscribe(observer: Observer): Subscription; + if (typeof this !== 'function') { + constructor = Observable; + } + else { + constructor = this; + } - /** - * Registers handlers for handling emitted values, error and completions from the observable, and - * executes the observable's subscriber function, which will take action to set up the underlying data stream. - * - * @param onNext A function to handle an emitted value. Value is passed in as the first parameter to the function. - * @param onError? A function to handle errors that occur during onNext, or during subscription. - * @param onComplete? A function that gets called when the subscription is complete, and will not send any more values. This function will also get called if an error occurs and onError is not defined. - * - * @return {Subscription} A Subscription object that can be used to manage the subscription. - */ - subscribe(onNext: (value: T) => any, onError?: (error: any) => any, onComplete?: (completeValue?: any) => void): Subscription; - /* istanbul ignore next */ - subscribe(observerOrNext: any, onError?: (error: any) => any, onComplete?: (compleeValue?: any) => void): Subscription { - throw new Error(); - } + const observableSymbol = (item as Observable)[ Symbol.observable ]; - /* istanbul ignore next */ - /** - * Create an Observable from a list of values. - * - * @param {...T} items The values to be emitted - * - * @return {Observable} An Observable that will emit the specified values - * - * @example - * - * let source = Observable.of(1, 2, 3); - * - * // will emit three separate values, 1, 2, and 3. - */ - static of(...items: T[]): Observable { - throw new Error(); - } + if (observableSymbol !== undefined) { + if (typeof observableSymbol !== 'function') { + throw new TypeError('Symbol.observable must be a function'); + } - /* istanbul ignore next */ - /** - * Create an Observable from another object. If the object is in itself Observable, the object will be returned. - * Otherwise, the value will be wrapped in an Observable. If the object is iterable, an Observable will be created - * that emits each item of the iterable. - * - * @param {Iterable | ArrayLike | ObservableObject} item The item to be turned into an Observable - * - * @return {Observable} An observable for the item you passed in - */ - static from(item: Iterable | ArrayLike | ObservableObject): Observable { - throw new Error(); - } - - /* istanbul ignore next */ - [Symbol.observable](): any { - throw new Error(); - } + const result: any = observableSymbol.call(item); + + if (result === undefined || result === null || typeof result === 'number' || typeof result === 'boolean' || typeof result === 'string') { + throw new TypeError('Return value of Symbol.observable must be object'); + } + + if (result.constructor && result.constructor === this || result instanceof Observable) { + return result; + } + else if (result.subscribe) { + return new constructor(result.subscribe); + } + else { + if (constructor.of) { + return constructor.of(result); + } + else { + return Observable.of(result); + } + } + } + else if (isIterable(item) || isArrayLike(item)) { + return new constructor((observer: SubscriptionObserver) => { + forOf(item, (o: any) => { + observer.next(o); + }); + observer.complete(); + }); + } + else { + throw new TypeError('Parameter is neither Observable nor Iterable'); + } + } + } + return Observable; + })(); } + +export default Observable; diff --git a/src/Promise.ts b/src/Promise.ts index 1bbde89..0f89827 100644 --- a/src/Promise.ts +++ b/src/Promise.ts @@ -3,7 +3,6 @@ import global from './global'; import { queueMicroTask } from './support/queue'; import { forOf, Iterable } from './iterator'; import './Symbol'; -import { hasClass } from './support/decorators'; import has from './support/has'; /** @@ -19,42 +18,23 @@ export interface Executor { (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void): void; } -module Shim { +export let ShimPromise: typeof Promise = global.Promise; - /** - * The State enum represents the possible states of a promise. - */ - export const enum State { +export const isThenable = function isThenable(value: any): value is PromiseLike { + return value && typeof value.then === 'function'; +}; + +if (!has('es6-promise')) { + const enum State { Fulfilled, Pending, Rejected } - /** - * Returns true if a given value has a `then` method. - * @param {any} value The value to check if is Thenable - * @returns {is PromiseLike} A type guard if the value is thenable - */ - export function isThenable(value: any): value is PromiseLike { - return value && typeof value.then === 'function'; - } - - /** - * Promise is a partial implementation of the ES2015 Promise specification. It relies on Promise to do some safety - * checks such as verifying that a Promise isn't resolved with itself. This class is exported for testability, and is - * not intended to be used directly. - * - * @borrows Promise.all as Promise.all - * @borrows Promise.race as Promise.race - * @borrows Promise.reject as Promise.reject - * @borrows Promise.resolve as Promise.resolve - * @borrows Promise#catch as Promise#catch - * @borrows Promise#then as Promise#then - */ - export class Promise implements Thenable { - static all(iterable: Iterable<(T | PromiseLike)> | (T | PromiseLike)[]): Promise { + global.Promise = ShimPromise = class Promise implements Thenable { + static all(iterable: Iterable<(any | PromiseLike)> | (any | PromiseLike)[]): Promise { return new this(function (resolve, reject) { - const values: T[] = []; + const values: any[] = []; let complete = 0; let total = 0; let populating = true; @@ -72,7 +52,7 @@ module Shim { resolve(values); } - function processItem(index: number, item: (T | PromiseLike)): void { + function processItem(index: number, item: any): void { ++total; if (isThenable(item)) { // If an item Promise rejects, this Promise is immediately rejected with the item @@ -85,7 +65,7 @@ module Shim { } let i = 0; - forOf(iterable, function (value: T | PromiseLike) { + forOf(iterable, function (value: any) { processItem(i, value); i++; }); @@ -110,7 +90,7 @@ module Shim { }); } - static reject(reason?: any): Promise { + static reject(reason?: any): Promise { return new this(function (resolve, reject) { reject(reason); }); @@ -124,6 +104,8 @@ module Shim { }); } + static [Symbol.species]: PromiseConstructor = ShimPromise as PromiseConstructor; + /** * Creates a new Promise. * @@ -274,189 +256,8 @@ module Shim { then: (onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null) => Promise; - [Symbol.toStringTag] = 'Promise'; - } -} - -@hasClass('es6-promise', global.Promise, Shim.Promise) -export default class Promise implements PromiseLike { - /** - * Creates a new Promise. - * - * @constructor - * - * @param executor - * The executor function is called immediately when the Promise is instantiated. It is responsible for - * starting the asynchronous operation when it is invoked. - * - * The executor must call either the passed `resolve` function when the asynchronous operation has completed - * successfully, or the `reject` function when the operation fails. - */ - /* istanbul ignore next */ - constructor(executor: Executor) { - } - - /** - * Converts an iterable object containing promises into a single promise that resolves to a new iterable object - * containing the fulfilled values of all the promises in the iterable, in the same order as the Promises in the - * iterable. Iterable values that are not promises are converted to promises using Promise.resolve. - * - * @example - * Promise.all([ Promise.resolve('foo'), 'bar' ]).then(function (value) { - * value[0] === 'foo'; // true - * value[1] === 'bar'; // true - * }); - * - * @example - * Promise.all({ - * foo: Promise.resolve('foo'), - * bar: 'bar' - * }).then((value) => { - * value.foo === 'foo'; // true - * value.bar === 'bar'; // true - * }); - */ - /* istanbul ignore next */ - static all(iterable: (T | PromiseLike)[] | Iterable<(T | PromiseLike)>): Promise { - throw new Error(); + [Symbol.toStringTag]: 'Promise' = 'Promise'; }; - - /** - * Converts an iterable object containing promises into a single promise that resolves or rejects as soon as one of - * the promises in the iterable resolves or rejects, with the value of the resolved or rejected promise. Values in - * the iterable that are not Promises are converted to Promises with Promise.resolve. - * - * @example - * Promise.race([ Promise.resolve('foo'), Promise.resolve('bar') ]).then((value) => { - * value === 'foo'; // true - * }); - * - * @example - * Promise.race({ - * foo: Promise.resolve('foo'), - * bar: Promise.resolve('bar') - * }).then((value) => { - * value === 'foo'; // true - * }); - */ - /* istanbul ignore next */ - static race(iterable: Iterable<(T | PromiseLike)> | (T | PromiseLike)[]): Promise { - throw new Error(); - } - - /** - * Creates a new promise that is rejected with the given error. - */ - /* istanbul ignore next */ - static reject(reason?: any): Promise { - throw new Error(); - } - - /** - * Creates a new promise that is resolved with the given value. - */ - static resolve(): Promise; - static resolve(value: (T | PromiseLike)): Promise; - /* istanbul ignore next */ - static resolve(value?: any): Promise { - throw new Error(); - } - - /** - * Adds a callback to the promise to be invoked when the asynchronous operation throws an error. - */ - catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise { - throw new Error(); - } - - then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise { - throw new Error(); - } } -global.Promise = has('es6-promise') ? global.Promise : Shim.Promise; - -declare global { - interface PromiseConstructor { - readonly prototype: Promise; - - /** - * Creates a new Promise. - * - * @constructor - * - * @param executor - * The executor function is called immediately when the Promise is instantiated. It is responsible for - * starting the asynchronous operation when it is invoked. - * - * The executor must call either the passed `resolve` function when the asynchronous operation has completed - * successfully, or the `reject` function when the operation fails. - */ - new (executor: Executor): Promise; - - /** - * Converts an iterable object containing promises into a single promise that resolves to a new iterable object - * containing the fulfilled values of all the promises in the iterable, in the same order as the Promises in the - * iterable. Iterable values that are not promises are converted to promises using Promise.resolve. - * - * @example - * Promise.all([ Promise.resolve('foo'), 'bar' ]).then(function (value) { - * value[0] === 'foo'; // true - * value[1] === 'bar'; // true - * }); - * - * @example - * Promise.all({ - * foo: Promise.resolve('foo'), - * bar: 'bar' - * }).then((value) => { - * value.foo === 'foo'; // true - * value.bar === 'bar'; // true - * }); - */ - all(iterable: (T | PromiseLike)[] | Iterable<(T | PromiseLike)>): Promise; - - /** - * Converts an iterable object containing promises into a single promise that resolves or rejects as soon as one of - * the promises in the iterable resolves or rejects, with the value of the resolved or rejected promise. Values in - * the iterable that are not Promises are converted to Promises with Promise.resolve. - * - * @example - * Promise.race([ Promise.resolve('foo'), Promise.resolve('bar') ]).then((value) => { - * value === 'foo'; // true - * }); - * - * @example - * Promise.race({ - * foo: Promise.resolve('foo'), - * bar: Promise.resolve('bar') - * }).then((value) => { - * value === 'foo'; // true - * }); - */ - race(iterable: Iterable<(T | PromiseLike)> | (T | PromiseLike)[]): Promise; - - /** - * Creates a new promise that is rejected with the given error. - */ - reject(reason?: any): Promise; - - /** - * Creates a new promise that is rejected with the given error. - */ - reject(reason?: any): Promise; - - /** - * Creates a new promise that is resolved with the given value. - */ - resolve(value: T | PromiseLike): Promise; - - /** - * Creates a new promise that is resolved with the given value. - */ - resolve(): Promise; - } - - /* tslint:disable:variable-name no-unused-variable */ - const Promise: PromiseConstructor; -} +export default ShimPromise; diff --git a/src/Set.ts b/src/Set.ts index f0ab636..9ff40c0 100644 --- a/src/Set.ts +++ b/src/Set.ts @@ -1,13 +1,113 @@ -import { ArrayLike } from './interfaces'; -import { hasClass } from './support/decorators'; import global from './global'; import { forOf, IterableIterator, Iterable, ShimIterator } from './iterator'; +import has from './support/has'; import './Symbol'; -export namespace Shim { - export class Set { +export interface Set { + /** + * Adds a `value` to the `Set` + * + * @param value The value to add to the set + * @returns The instance of the `Set` + */ + add(value: T): this; + + /** + * Removes all the values from the `Set`. + */ + clear(): void; + + /** + * Removes a `value` from the set + * + * @param value The value to be removed + * @returns `true` if the value was removed + */ + delete(value: T): boolean; + + /** + * Returns an iterator that yields each entry. + * + * @return An iterator for each key/value pair in the instance. + */ + entries(): IterableIterator<[T, T]>; + + /** + * Executes a given function for each set entry. The function + * is invoked with three arguments: the element value, the + * element key, and the associated `Set` instance. + * + * @param callbackfn The function to execute for each map entry, + * @param thisArg The value to use for `this` for each execution of the calback + */ + forEach(callbackfn: (value: T, value2: T, set: Set) => void, thisArg?: any): void; + + /** + * Identifies if a value is part of the set. + * + * @param value The value to check + * @returns `true` if the value is part of the set otherwise `false` + */ + has(value: T): boolean; + + /** + * Despite its name, returns an iterable of the values in the set, + */ + keys(): IterableIterator; + + /** + * Returns the number of values in the `Set`. + */ + readonly size: number; + + /** + * Returns an iterable of values in the set. + */ + values(): IterableIterator; + + /** Iterates over values in the set. */ + [Symbol.iterator](): IterableIterator; + + readonly [Symbol.toStringTag]: 'Set'; +} + +export interface SetConstructor { + /** + * Creates a new Set + * + * @constructor + */ + new (): Set; + + /** + * Creates a new Set + * + * @constructor + * + * @param iterator The iterable structure to initialize the set with + */ + new (iterator?: T[]): Set; + + /** + * Creates a new Set + * + * @constructor + * + * @param iterator The iterable structure to initialize the set with + */ + new (iterator: Iterable): Set; + + readonly prototype: Set; +} + +export let Set: SetConstructor = global.Set; + +if (!has('es6-set')) { + Set = class Set { private readonly _setData: T[] = []; + static [Symbol.species] = Set; + constructor(iterable?: ArrayLike | Iterable) { if (iterable) { forOf(iterable, (value) => this.add(value)); @@ -68,35 +168,8 @@ export namespace Shim { return new ShimIterator(this._setData); }; - [Symbol.toStringTag] = 'Set'; - } + [Symbol.toStringTag]: 'Set' = 'Set'; + }; } -@hasClass('es6-set', global.Set, Shim.Set) -export default class Set { - /* istanbul ignore next */ - constructor(iterable?: ArrayLike | Iterable) { }; - - /* istanbul ignore next */ - add(value: T): this { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - clear(): void { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - delete(value: T): boolean { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - entries(): IterableIterator<[T, T]> { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - forEach(callbackfn: (value: T, index: T, set: Set) => void, thisArg?: any): void { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - has(value: T): boolean { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - keys(): IterableIterator { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - get size(): number { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - values(): IterableIterator { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - [Symbol.iterator](): IterableIterator { throw new Error('Abstract method'); }; - /* istanbul ignore next */ - [Symbol.toStringTag] = 'Set'; -} +export default Set; diff --git a/src/Symbol.ts b/src/Symbol.ts index bdc883f..13b02d5 100644 --- a/src/Symbol.ts +++ b/src/Symbol.ts @@ -8,29 +8,28 @@ declare global { } } -export namespace Shim { - /* tslint:disable-next-line:variable-name */ - let Symbol: SymbolConstructor; - /* tslint:disable-next-line:variable-name */ - let InternalSymbol: SymbolConstructor; +export let Symbol: SymbolConstructor = global.Symbol; + +if (!has('es6-symbol')) { + /** + * Throws if the value is not a symbol, used internally within the Shim + * @param {any} value The value to check + * @return {symbol} Returns the symbol or throws + */ + const validateSymbol = function validateSymbol(value: any): symbol { + if (!isSymbol(value)) { + throw new TypeError(value + ' is not a symbol'); + } + return value; + }; const defineProperties = Object.defineProperties; - const defineProperty = Object.defineProperty; + const defineProperty: (o: any, p: string | symbol, attributes: PropertyDescriptor & ThisType) => any = Object.defineProperty; const create = Object.create; const objPrototype = Object.prototype; - interface GlobalSymbols { - [key: string]: symbol; - } - - const globalSymbols: GlobalSymbols = {}; - - interface TypedPropertyDescriptor extends PropertyDescriptor { - value?: T; - get? (): T; - set? (v: T): void; - } + const globalSymbols: { [key: string]: symbol } = {}; const getSymbolName = (function () { const created = create(null); @@ -58,14 +57,14 @@ export namespace Shim { }; }()); - InternalSymbol = function Symbol(this: any, description?: string|number): symbol { + const InternalSymbol = function Symbol(this: any, description?: string|number): symbol { if (this instanceof InternalSymbol) { throw new TypeError('TypeError: Symbol is not a constructor'); } return Symbol(description); - } as SymbolConstructor; + }; - Symbol = function Symbol(this: Symbol, description?: string|number): symbol { + Symbol = global.Symbol = function Symbol(this: Symbol, description?: string|number): symbol { if (this instanceof Symbol) { throw new TypeError('TypeError: Symbol is not a constructor'); } @@ -77,27 +76,6 @@ export namespace Shim { }); } as SymbolConstructor; - /** - * A custom guard function that determines if an object is a symbol or not - * @param {any} value The value to check to see if it is a symbol or not - * @return {is symbol} Returns true if a symbol or not (and narrows the type guard) - */ - export function isSymbol(value: any): value is symbol { - return (value && ((typeof value === 'symbol') || (value['@@toStringTag'] === 'Symbol'))) || false; - } - - /** - * Throws if the value is not a symbol, used internally within the Shim - * @param {any} value The value to check - * @return {symbol} Returns the symbol or throws - */ - function validateSymbol(value: any): symbol { - if (!isSymbol(value)) { - throw new TypeError(value + ' is not a symbol'); - } - return value; - } - /* Decorate the Symbol function with the appropriate properties */ defineProperty(Symbol, 'for', getValueDescriptor(function (key: string): symbol { if (globalSymbols[key]) { @@ -141,29 +119,30 @@ export namespace Shim { valueOf: getValueDescriptor(function (this: Symbol) { return validateSymbol(this); }) }); - defineProperty(Symbol.prototype, Symbol.toPrimitive, getValueDescriptor(function (this: Symbol) { return validateSymbol(this); })); - defineProperty(Symbol.prototype, Symbol.toStringTag, getValueDescriptor('Symbol', false, false, true)); - - defineProperty(InternalSymbol.prototype, Symbol.toPrimitive, getValueDescriptor(( Symbol).prototype[Symbol.toPrimitive], false, false, true)); - defineProperty(InternalSymbol.prototype, Symbol.toStringTag, getValueDescriptor(( Symbol).prototype[Symbol.toStringTag], false, false, true)); + defineProperty(Symbol.prototype, Symbol.toPrimitive, getValueDescriptor(function (this: Symbol) { return validateSymbol(this); })); + defineProperty(Symbol.prototype, Symbol.toStringTag, getValueDescriptor('Symbol', false, false, true)); - /* tslint:disable-next-line:variable-name */ - export const Exposed = Symbol; + defineProperty(InternalSymbol.prototype, Symbol.toPrimitive, getValueDescriptor(( Symbol).prototype[Symbol.toPrimitive], false, false, true)); + defineProperty(InternalSymbol.prototype, Symbol.toStringTag, getValueDescriptor(( Symbol).prototype[Symbol.toStringTag], false, false, true)); } -/* tslint:disable-next-line:variable-name */ -const SymbolShim: SymbolConstructor = has('es6-symbol') ? global.Symbol : global.Symbol = Shim.Exposed; +/** + * A custom guard function that determines if an object is a symbol or not + * @param {any} value The value to check to see if it is a symbol or not + * @return {is symbol} Returns true if a symbol or not (and narrows the type guard) + */ +export function isSymbol(value: any): value is symbol { + return (value && ((typeof value === 'symbol') || (value['@@toStringTag'] === 'Symbol'))) || false; +} /** * Fill any missing well known symbols if the native Symbol is missing them */ [ 'hasInstance', 'isConcatSpreadable', 'iterator', 'species', 'replace', 'search', 'split', 'match', 'toPrimitive', 'toStringTag', 'unscopables', 'observable' ].forEach((wellKnown) => { - if (!( Symbol)[wellKnown]) { + if (!(Symbol as any)[wellKnown]) { Object.defineProperty(Symbol, wellKnown, getValueDescriptor(Symbol.for(wellKnown), false, false)); } }); -export const isSymbol = Shim.isSymbol; - -export default SymbolShim; +export default Symbol; diff --git a/src/WeakMap.ts b/src/WeakMap.ts index 605da07..8719757 100644 --- a/src/WeakMap.ts +++ b/src/WeakMap.ts @@ -1,22 +1,89 @@ -import { ArrayLike } from './interfaces'; -import { hasClass } from './support/decorators'; import global from './global'; import { forOf, Iterable } from './iterator'; +import has from './support/has'; import './Symbol'; -module Shim { - const DELETED: any = {}; +export interface WeakMap { + /** + * Remove a `key` from the map + * + * @param key The key to remove + * @return `true` if the value was removed, otherwise `false` + */ + delete(key: K): boolean; + + /** + * Retrieve the value, based on the supplied `key` + * + * @param key The key to retrieve the `value` for + * @return the `value` based on the `key` if found, otherwise `false` + */ + get(key: K): V | undefined; + + /** + * Determines if a `key` is present in the map + * + * @param key The `key` to check + * @return `true` if the key is part of the map, otherwise `false`. + */ + has(key: K): boolean; + + /** + * Set a `value` for a particular `key`. + * + * @param key The `key` to set the `value` for + * @param value The `value` to set + * @return the instances + */ + set(key: K, value: V): this; + + readonly [Symbol.toStringTag]: 'WeakMap'; +} - interface Entry { - key: K; - value: V; - } +export interface WeakMapConstructor { + /** + * Create a new instance of a `WeakMap` + * + * @constructor + */ + new (): WeakMap; + + /** + * Create a new instance of a `WeakMap` + * + * @constructor + * + * @param iterable An iterable that contains yields up key/value pair entries + */ + new (iterable?: [K, V][]): WeakMap; + + /** + * Create a new instance of a `WeakMap` + * + * @constructor + * + * @param iterable An iterable that contains yields up key/value pair entries + */ + new (iterable: Iterable<[K, V]>): WeakMap; + + readonly prototype: WeakMap; +} - function getUID(): number { +export let WeakMap: WeakMapConstructor = global.WeakMap; + +interface Entry { + key: K; + value: V; +} + +if (!has('es6-weakmap')) { + const DELETED: any = {}; + + const getUID = function getUID(): number { return Math.floor(Math.random() * 100000000); - } + }; - let generateName = (function () { + const generateName = (function () { let startId = Math.floor(Date.now() % 100000000); return function generateName(): string { @@ -24,7 +91,7 @@ module Shim { }; })(); - export class WeakMap { + WeakMap = class WeakMap { private readonly _name: string; private readonly _frozenEntries: Entry[]; @@ -104,7 +171,7 @@ module Shim { return false; } - set(key: any, value?: any): Shim.WeakMap { + set(key: any, value?: any): this { if (!key || (typeof key !== 'object' && typeof key !== 'function')) { throw new TypeError('Invalid value used as weak map key'); } @@ -127,23 +194,8 @@ module Shim { return this; } - [Symbol.toStringTag] = 'WeakMap'; - } + [Symbol.toStringTag]: 'WeakMap' = 'WeakMap'; + }; } -@hasClass('es6-weakmap', global.WeakMap, Shim.WeakMap) -export default class WeakMap { - /* istanbul ignore next */ - constructor(iterable?: ArrayLike<[K, V]> | Iterable<[K, V]>) {} - - /* istanbul ignore next */ - delete(key: K): boolean { throw new Error(); } - /* istanbul ignore next */ - get(key: K): V { throw new Error(); } - /* istanbul ignore next */ - has(key: K): boolean { throw new Error(); } - /* istanbul ignore next */ - set(key: K, value?: V): WeakMap { throw new Error(); } - /* istanbul ignore next */ - [Symbol.toStringTag] = 'WeakMap'; -} +export default WeakMap; diff --git a/src/array.ts b/src/array.ts index 5569651..353d594 100644 --- a/src/array.ts +++ b/src/array.ts @@ -1,8 +1,8 @@ -import { ArrayLike } from './interfaces'; +import global from './global'; +import { forOf, isArrayLike, isIterable, Iterable } from './iterator'; +import { MAX_SAFE_INTEGER } from './number'; import has from './support/has'; import { wrapNative } from './support/util'; -import { forOf, isArrayLike, isIterable, Iterable } from './iterator'; -import { MAX_SAFE_INTEGER as maxSafeInteger } from './number'; export interface MapCallback { /** @@ -25,59 +25,160 @@ export interface FindCallback { (element: T, index: number, array: ArrayLike): boolean; } +interface WritableArrayLike { + readonly length: number; + [n: number]: T; +} + +/* ES6 Array static methods */ + +export interface From { + /** + * The Array.from() method creates a new Array instance from an array-like or iterable object. + * + * @param source An array-like or iterable object to convert to an array + * @param mapFunction A map function to call on each element in the array + * @param thisArg The execution context for the map function + * @return The new Array + */ + (source: ArrayLike | Iterable, mapFunction: MapCallback, thisArg?: any): Array; + + /** + * The Array.from() method creates a new Array instance from an array-like or iterable object. + * + * @param source An array-like or iterable object to convert to an array + * @return The new Array + */ + (source: ArrayLike | Iterable): Array; +} + +export let from: From; + /** - * Ensures a non-negative, non-infinite, safe integer. + * Creates a new array from the function parameters. * - * @param length The number to validate - * @return A proper length + * @param arguments Any number of arguments for the array + * @return An array from the given arguments */ -function toLength(length: number): number { - length = Number(length); - if (isNaN(length)) { - return 0; - } - if (isFinite(length)) { - length = Math.floor(length); - } - // Ensure a non-negative, real, safe integer - return Math.min(Math.max(length, 0), maxSafeInteger); -} +export let of: (...items: T[]) => Array; + +/* ES6 Array instance methods */ /** - * From ES6 7.1.4 ToInteger() + * Copies data internally within an array or array-like object. * - * @param value A value to convert - * @return An integer + * @param target The target array-like object + * @param offset The index to start copying values to; if negative, it counts backwards from length + * @param start The first (inclusive) index to copy; if negative, it counts backwards from length + * @param end The last (exclusive) index to copy; if negative, it counts backwards from length + * @return The target */ -function toInteger(value: any): number { - value = Number(value); - if (isNaN(value)) { - return 0; - } - if (value === 0 || !isFinite(value)) { - return value; - } - - return (value > 0 ? 1 : -1) * Math.floor(Math.abs(value)); -} +export let copyWithin: (target: ArrayLike, offset: number, start: number, end?: number) => ArrayLike; + +/** + * Fills elements of an array-like object with the specified value. + * + * @param target The target to fill + * @param value The value to fill each element of the target with + * @param start The first index to fill + * @param end The (exclusive) index at which to stop filling + * @return The filled target + */ +export let fill: (target: ArrayLike, value: T, start?: number, end?: number) => ArrayLike; /** - * Normalizes an offset against a given length, wrapping it if negative. + * Finds and returns the first instance matching the callback or undefined if one is not found. * - * @param value The original offset - * @param length The total length to normalize against - * @return If negative, provide a distance from the end (length); otherwise provide a distance from 0 + * @param target An array-like object + * @param callback A function returning if the current value matches a criteria + * @param thisArg The execution context for the find function + * @return The first element matching the callback, or undefined if one does not exist */ -function normalizeOffset(value: number, length: number): number { - return value < 0 ? Math.max(length + value, 0) : Math.min(value, length); -} +export let find: (target: ArrayLike, callback: FindCallback, thisArg?: {}) => T | undefined; /** - * A namespace that contains the polyfilled functionality that is then exported below, depending on if - * the functionality is required or not. + * Performs a linear search and returns the first index whose value satisfies the passed callback, + * or -1 if no values satisfy it. + * + * @param target An array-like object + * @param callback A function returning true if the current value satisfies its criteria + * @param thisArg The execution context for the find function + * @return The first index whose value satisfies the passed callback, or -1 if no values satisfy it */ -export namespace Shim { - export function from(this: ArrayConstructor, arrayLike: Iterable | ArrayLike, mapFunction?: MapCallback, thisArg?: any): Array { +export let findIndex: (target: ArrayLike, callback: FindCallback, thisArg?: {}) => number; + +/* ES7 Array instance methods */ + +/** + * Determines whether an array includes a given value + * + * @param target the target array-like object + * @param searchElement the item to search for + * @param fromIndex the starting index to search from + * @return `true` if the array includes the element, otherwise `false` + */ +export let includes: (target: ArrayLike, searchElement: T, fromIndex?: number) => boolean; + +if (has('es6-array') && has('es6-array-fill')) { + from = global.Array.from; + of = global.Array.of; + copyWithin = wrapNative(global.Array.prototype.copyWithin); + fill = wrapNative(global.Array.prototype.fill); + find = wrapNative(global.Array.prototype.find); + findIndex = wrapNative(global.Array.prototype.findIndex); +} +else { + // It is only older versions of Safari/iOS that have a bad fill implementation and so aren't in the wild + // To make things easier, if there is a bad fill implementation, the whole set of functions will be filled + + /** + * Ensures a non-negative, non-infinite, safe integer. + * + * @param length The number to validate + * @return A proper length + */ + const toLength = function toLength(length: number): number { + length = Number(length); + if (isNaN(length)) { + return 0; + } + if (isFinite(length)) { + length = Math.floor(length); + } + // Ensure a non-negative, real, safe integer + return Math.min(Math.max(length, 0), MAX_SAFE_INTEGER); + }; + + /** + * From ES6 7.1.4 ToInteger() + * + * @param value A value to convert + * @return An integer + */ + const toInteger = function toInteger(value: any): number { + value = Number(value); + if (isNaN(value)) { + return 0; + } + if (value === 0 || !isFinite(value)) { + return value; + } + + return (value > 0 ? 1 : -1) * Math.floor(Math.abs(value)); + }; + + /** + * Normalizes an offset against a given length, wrapping it if negative. + * + * @param value The original offset + * @param length The total length to normalize against + * @return If negative, provide a distance from the end (length); otherwise provide a distance from 0 + */ + const normalizeOffset = function normalizeOffset(value: number, length: number): number { + return value < 0 ? Math.max(length + value, 0) : Math.min(value, length); + }; + + from = function from(this: ArrayConstructor, arrayLike: Iterable | ArrayLike, mapFunction?: MapCallback, thisArg?: any): Array { if (arrayLike == null) { throw new TypeError('from: requires an array-like object'); } @@ -107,13 +208,13 @@ export namespace Shim { } return array; - } + }; - export function of(...items: T[]): Array { + of = function of(...items: T[]): Array { return Array.prototype.slice.call(items); - } + }; - export function copyWithin(target: ArrayLike, offset: number, start: number, end?: number): ArrayLike { + copyWithin = function copyWithin(target: ArrayLike, offset: number, start: number, end?: number): ArrayLike { if (target == null) { throw new TypeError('copyWithin: target must be an array-like object'); } @@ -133,10 +234,10 @@ export namespace Shim { while (count > 0) { if (start in target) { - target[offset] = target[start]; + (target as WritableArrayLike)[offset] = target[start]; } else { - delete target[offset]; + delete (target as WritableArrayLike)[offset]; } offset += direction; @@ -145,26 +246,26 @@ export namespace Shim { } return target; - } + }; - export function fill(target: ArrayLike, value: any, start?: number, end?: number): ArrayLike { + fill = function fill(target: ArrayLike, value: any, start?: number, end?: number): ArrayLike { const length = toLength(target.length); let i = normalizeOffset(toInteger(start), length); end = normalizeOffset(end === undefined ? length : toInteger(end), length); while (i < end) { - target[i++] = value; + (target as WritableArrayLike)[i++] = value; } return target; - } + }; - export function find(target: ArrayLike, callback: FindCallback, thisArg?: {}): T | undefined { + find = function find(target: ArrayLike, callback: FindCallback, thisArg?: {}): T | undefined { const index = findIndex(target, callback, thisArg); return index !== -1 ? target[index] : undefined; - } + }; - export function findIndex(target: ArrayLike, callback: FindCallback, thisArg?: {}): number { + findIndex = function findIndex(target: ArrayLike, callback: FindCallback, thisArg?: {}): number { const length = toLength(target.length); if (!callback) { @@ -182,9 +283,32 @@ export namespace Shim { } return -1; - } + }; +} + +if (has('es7-array')) { + includes = wrapNative(global.Array.prototype.includes); +} +else { + /** + * Ensures a non-negative, non-infinite, safe integer. + * + * @param length The number to validate + * @return A proper length + */ + const toLength = function toLength(length: number): number { + length = Number(length); + if (isNaN(length)) { + return 0; + } + if (isFinite(length)) { + length = Math.floor(length); + } + // Ensure a non-negative, real, safe integer + return Math.min(Math.max(length, 0), MAX_SAFE_INTEGER); + }; - export function includes(target: ArrayLike, searchElement: T, fromIndex: number = 0): boolean { + includes = function includes(target: ArrayLike, searchElement: T, fromIndex: number = 0): boolean { let len = toLength(target.length); for (let i = fromIndex; i < len; ++i) { @@ -196,108 +320,5 @@ export namespace Shim { } return false; - } + }; } - -/* ES6 Array static methods */ - -export interface From { - /** - * The Array.from() method creates a new Array instance from an array-like or iterable object. - * - * @param source An array-like or iterable object to convert to an array - * @param mapFunction A map function to call on each element in the array - * @param thisArg The execution context for the map function - * @return The new Array - */ - (source: ArrayLike | Iterable, mapFunction: MapCallback, thisArg?: any): Array; - - /** - * The Array.from() method creates a new Array instance from an array-like or iterable object. - * - * @param source An array-like or iterable object to convert to an array - * @return The new Array - */ - (source: ArrayLike | Iterable): Array; -} - -export const from: From = has('es6-array-from') - ? ( Array).from - : Shim.from; - -/** - * Creates a new array from the function parameters. - * - * @param arguments Any number of arguments for the array - * @return An array from the given arguments - */ -export const of: (...items: T[]) => Array = has('es6-array-of') - ? ( Array).of - : Shim.of; - -/* ES6 Array instance methods */ - -/** - * Copies data internally within an array or array-like object. - * - * @param target The target array-like object - * @param offset The index to start copying values to; if negative, it counts backwards from length - * @param start The first (inclusive) index to copy; if negative, it counts backwards from length - * @param end The last (exclusive) index to copy; if negative, it counts backwards from length - * @return The target - */ -export const copyWithin: (target: ArrayLike, offset: number, start: number, end?: number) => ArrayLike = has('es6-array-copywithin') - ? wrapNative(( Array.prototype).copyWithin) - : Shim.copyWithin; - -/** - * Fills elements of an array-like object with the specified value. - * - * @param target The target to fill - * @param value The value to fill each element of the target with - * @param start The first index to fill - * @param end The (exclusive) index at which to stop filling - * @return The filled target - */ -export const fill: (target: ArrayLike, value: T, start?: number, end?: number) => ArrayLike = has('es6-array-fill') - ? wrapNative(( Array.prototype).fill) - : Shim.fill; - -/** - * Finds and returns the first instance matching the callback or undefined if one is not found. - * - * @param target An array-like object - * @param callback A function returning if the current value matches a criteria - * @param [thisArg] The execution context for the find function - * @return The first element matching the callback, or undefined if one does not exist - */ -export const find: (target: ArrayLike, callback: FindCallback, thisArg?: {}) => T = has('es6-array-find') - ? wrapNative(( Array.prototype).find) - : Shim.find; - -/** - * Performs a linear search and returns the first index whose value satisfies the passed callback, - * or -1 if no values satisfy it. - * - * @param target An array-like object - * @param callback A function returning true if the current value satisfies its criteria - * @param [thisArg] The execution context for the find function - * @return The first index whose value satisfies the passed callback, or -1 if no values satisfy it - */ -export const findIndex: (target: ArrayLike, callback: FindCallback, thisArg?: {}) => number = has('es6-array-findindex') - ? wrapNative(( Array.prototype).findIndex) - : Shim.findIndex; - -/* ES7 Array instance methods */ - -/** - * Determines whether an array includes a given value - * - * @param target the target array-like object - * @param searchElement the item to search for - * @param fromIndex the starting index to search from - * @return `true` if the array includes the element, otherwise `false` - */ -export const includes: (target: ArrayLike, searchElement: T, fromIndex?: number) => boolean = has('es7-array-includes') - ? wrapNative(( Array.prototype).includes) - : Shim.includes; diff --git a/src/interfaces.d.ts b/src/interfaces.d.ts index e9cf717..5940b21 100644 --- a/src/interfaces.d.ts +++ b/src/interfaces.d.ts @@ -1,19 +1,12 @@ -/** - * Thenable represents any object with a callable `then` property. - */ -export interface Thenable { - then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; - then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => void): Thenable; -} - -export interface ArrayLike { - length: number; - [n: number]: T; -} - export interface Handle { /** * Perform the destruction/cleanup logic associated with this handle */ destroy(): void; } + +/** + * Something that is _thenable_ + * @deprecated Use `PromiseLike` from TypeScript lib instead + */ +export type Thenable = PromiseLike; diff --git a/src/iterator.ts b/src/iterator.ts index 9d45f00..76e0e4c 100644 --- a/src/iterator.ts +++ b/src/iterator.ts @@ -1,4 +1,3 @@ -import { ArrayLike } from './interfaces'; import { HIGH_SURROGATE_MIN, HIGH_SURROGATE_MAX } from './string'; import './Symbol'; @@ -24,8 +23,7 @@ export interface IterableIterator extends Iterator { const staticDone: IteratorResult = { done: true, value: undefined }; /** - * A class that provides "shims" an iterator interface on array like - * objects. + * A class that _shims_ an iterator interface on array like objects. */ export class ShimIterator { private _list: ArrayLike; diff --git a/src/math.ts b/src/math.ts index 399efd8..cf05a22 100644 --- a/src/math.ts +++ b/src/math.ts @@ -1,130 +1,8 @@ import has from './support/has'; -const FRACTION_UNITS = Math.pow(2, 23); -const MAX_FLOAT32 = 3.4028234663852886e+38; -const MIN_FLOAT32 = 1.401298464324817e-45; - -export namespace Shim { - export function acosh(n: number): number { - return Math.log(n + Math.sqrt(n * n - 1)); - } - - export function asinh(n: number): number { - if (n === -Infinity) { - return n; - } - else { - return Math.log(n + Math.sqrt(n * n + 1)); - } - } - - export function atanh(n: number): number { - return Math.log((1 + n) / (1 - n)) / 2; - } - - export function cbrt(n: number): number { - const y = Math.pow(Math.abs(n), 1 / 3); - return n < 0 ? -y : y; - } - - export function clz32(n: number): number { - n = Number(n) >>> 0; - return n ? 32 - n.toString(2).length : 32; - } - - export function cosh(n: number): number { - const m = Math.exp(n); - return (m + 1 / m) / 2; - } - - export function expm1(n: number): number { - return Math.exp(n) - 1; - } - - export const fround: (n: number) => number = has('float32array') ? function (n: number): number { - return new Float32Array([n])[0]; - } : - function (n: number): number { - // Further fallback for IE9, which doesn't support Float32Array. - // This gives a fair approximation in most cases. - - if (n === 0 || !isFinite(n)) { - return n; - } - if (Math.abs(n) > MAX_FLOAT32) { - return n > 0 ? Infinity : -Infinity; - } - if (Math.abs(n) < MIN_FLOAT32) { - return 0; - } - const exponent = Math.floor(log2(Math.abs(n))); - return (Math.round((n / Math.pow(2, exponent) - 1) * FRACTION_UNITS) / FRACTION_UNITS + 1) * Math.pow(2, exponent); - }; - - export function hypot(...args: number[]): number { - // See: http://mzl.la/1HDi6xP - let n = 0; - - for (let arg of args) { - if (arg === Infinity || arg === -Infinity) { - return Infinity; - } - n += arg * arg; - } - return Math.sqrt(n); - } - - export function imul(n: number, m: number): number { - // See: http://mzl.la/1K279FK - const ah = (n >>> 16) & 0xffff; - const al = n & 0xffff; - const bh = (m >>> 16) & 0xffff; - const bl = m & 0xffff; - return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0); - } - - export function log2(n: number): number { - return Math.log(n) / Math.LN2; - } - - export function log10(n: number): number { - return Math.log(n) / Math.LN10; - } - - export function log1p(n: number): number { - return Math.log(1 + n); - } - - export function sign(n: number): number { - n = Number(n); - if (n === 0 || n !== n) { - return n; - } - return n > 0 ? 1 : -1; - } - - export function sinh(n: number): number { - const m = Math.exp(n); - return (m - 1 / m) / 2; - } - - export function tanh(n: number): number { - if (n === Infinity) { - return 1; - } - else if (n === -Infinity) { - return -1; - } - else { - const y = Math.exp(2 * n); - return (y - 1) / (y + 1); - } - } - - export function trunc(n: number): number { - return n < 0 ? Math.ceil(n) : Math.floor(n); - } -} +export const FRACTION_UNITS = Math.pow(2, 23); +export const MAX_FLOAT32 = 3.4028234663852886e+38; +export const MIN_FLOAT32 = 1.401298464324817e-45; /** * Returns the hyperbolic arccosine of a number. @@ -132,9 +10,7 @@ export namespace Shim { * @param n The number to use in calculation * @return The result */ -export const acosh: (n: number) => number = has('es6-math-acosh') - ? ( Math).acosh - : Shim.acosh; +export let acosh: (n: number) => number = ( Math).acosh; /** * Returns the hyperbolic arcsine of a number. @@ -142,9 +18,7 @@ export const acosh: (n: number) => number = has('es6-math-acosh') * @param n The number to use in calculation * @return The result */ -export const asinh: (n: number) => number = 'asinh' in Math - ? ( Math).asinh - : Shim.asinh; +export let asinh: (n: number) => number = ( Math).asinh; /** * Returns the hyperbolic arctangent of a number. @@ -152,9 +26,7 @@ export const asinh: (n: number) => number = 'asinh' in Math * @param n The number to use in calculation * @return The result */ -export const atanh: (n: number) => number = 'atanh' in Math - ? ( Math).atanh - : Shim.atanh; +export let atanh: (n: number) => number = ( Math).atanh; /** * Returns the cube root of a number. @@ -162,9 +34,7 @@ export const atanh: (n: number) => number = 'atanh' in Math * @param n The number to use in calculation * @return The result */ -export const cbrt: (n: number) => number = 'cbrt' in Math - ? ( Math).cbrt - : Shim.cbrt; +export let cbrt: (n: number) => number = ( Math).cbrt; /** * Returns the number of leading zero bits in the 32-bit @@ -173,9 +43,7 @@ export const cbrt: (n: number) => number = 'cbrt' in Math * @param n The number to use in calculation * @return The result */ -export const clz32: (n: number) => number = 'clz32' in Math - ? ( Math).clz32 - : Shim.clz32; +export let clz32: (n: number) => number = ( Math).clz32; /** * Returns the hyperbolic cosine of a number. @@ -183,9 +51,7 @@ export const clz32: (n: number) => number = 'clz32' in Math * @param n The number to use in calculation * @return The result */ -export const cosh: (n: number) => number = 'cosh' in Math - ? ( Math).cosh - : Shim.cosh; +export let cosh: (n: number) => number = ( Math).cosh; /** * Returns e raised to the specified power minus one. @@ -193,9 +59,7 @@ export const cosh: (n: number) => number = 'cosh' in Math * @param n The number to use in calculation * @return The result */ -export const expm1: (n: number) => number = 'expm1' in Math - ? ( Math).expm1 - : Shim.expm1; +export let expm1: (n: number) => number = ( Math).expm1; /** * Returns the nearest single-precision float representation of a number. @@ -203,18 +67,14 @@ export const expm1: (n: number) => number = 'expm1' in Math * @param n The number to use in calculation * @return The result */ -export const fround: (n: number) => number = 'fround' in Math - ? ( Math).fround - : Shim.fround; +export let fround: (n: number) => number = ( Math).fround; /** * Returns the square root of the sum of squares of its arguments. * * @return The result */ -export const hypot: (...args: number[]) => number = 'hypot' in Math - ? ( Math).hypot - : Shim.hypot; +export let hypot: (...args: number[]) => number = ( Math).hypot; /** * Returns the result of the 32-bit multiplication of the two parameters. @@ -223,9 +83,7 @@ export const hypot: (...args: number[]) => number = 'hypot' in Math * @param m The number to use in calculation * @return The result */ -export const imul: (n: number, m: number) => number = has('es6-math-imul') - ? ( Math).imul - : Shim.imul; +export let imul: (n: number, m: number) => number = ( Math).imul; /** * Returns the base 2 logarithm of a number. @@ -233,9 +91,7 @@ export const imul: (n: number, m: number) => number = has('es6-math-imul') * @param n The number to use in calculation * @return The result */ -export const log2: (n: number) => number = 'log2' in Math - ? ( Math).log2 - : Shim.log2; +export let log2: (n: number) => number = ( Math).log2; /** * Returns the base 10 logarithm of a number. @@ -243,9 +99,7 @@ export const log2: (n: number) => number = 'log2' in Math * @param n The number to use in calculation * @return The result */ -export const log10: (n: number) => number = 'log10' in Math - ? ( Math).log10 - : Shim.log10; +export let log10: (n: number) => number = ( Math).log10; /** * Returns the natural logarithm of 1 + a number. @@ -253,9 +107,7 @@ export const log10: (n: number) => number = 'log10' in Math * @param n The number to use in calculation * @return The result */ -export const log1p: (n: number) => number = 'log1p' in Math - ? ( Math).log1p - : Shim.log1p; +export let log1p: (n: number) => number = ( Math).log1p; /** * Returns the sign of a number, indicating whether the number is positive. @@ -263,9 +115,7 @@ export const log1p: (n: number) => number = 'log1p' in Math * @param n The number to use in calculation * @return 1 if the number is positive, -1 if the number is negative, or 0 if the number is 0 */ -export const sign: (n: number) => number = 'sign' in Math - ? ( Math).sign - : Shim.sign; +export let sign: (n: number) => number = ( Math).sign; /** * Returns the hyperbolic sine of a number. @@ -273,9 +123,7 @@ export const sign: (n: number) => number = 'sign' in Math * @param n The number to use in calculation * @return The result */ -export const sinh: (n: number) => number = 'sinh' in Math - ? ( Math).sinh - : Shim.sinh; +export let sinh: (n: number) => number = ( Math).sinh; /** * Returns the hyperbolic tangent of a number. @@ -283,9 +131,7 @@ export const sinh: (n: number) => number = 'sinh' in Math * @param n The number to use in calculation * @return The result */ -export const tanh: (n: number) => number = 'tanh' in Math - ? ( Math).tanh - : Shim.tanh; +export let tanh: (n: number) => number = ( Math).tanh; /** * Returns the integral part of a number by removing any fractional digits. @@ -293,6 +139,112 @@ export const tanh: (n: number) => number = 'tanh' in Math * @param n The number to use in calculation * @return The result */ -export const trunc: (n: number) => number = 'trunc' in Math - ? ( Math).trunc - : Shim.trunc; +export let trunc: (n: number) => number = ( Math).trunc; + +if (!has('es6-math')) { + acosh = function acosh(n: number): number { + return Math.log(n + Math.sqrt(n * n - 1)); + }; + + asinh = function asinh(n: number): number { + if (n === -Infinity) { + return n; + } + else { + return Math.log(n + Math.sqrt(n * n + 1)); + } + }; + + atanh = function atanh(n: number): number { + return Math.log((1 + n) / (1 - n)) / 2; + }; + + cbrt = function cbrt(n: number): number { + const y = Math.pow(Math.abs(n), 1 / 3); + return n < 0 ? -y : y; + }; + + clz32 = function clz32(n: number): number { + n = Number(n) >>> 0; + return n ? 32 - n.toString(2).length : 32; + }; + + cosh = function cosh(n: number): number { + const m = Math.exp(n); + return (m + 1 / m) / 2; + }; + + expm1 = function expm1(n: number): number { + return Math.exp(n) - 1; + }; + + fround = function (n: number): number { + return new Float32Array([n])[0]; + }; + + hypot = function hypot(...args: number[]): number { + // See: http://mzl.la/1HDi6xP + let n = 0; + + for (let arg of args) { + if (arg === Infinity || arg === -Infinity) { + return Infinity; + } + n += arg * arg; + } + return Math.sqrt(n); + }; + + log2 = function log2(n: number): number { + return Math.log(n) / Math.LN2; + }; + + log10 = function log10(n: number): number { + return Math.log(n) / Math.LN10; + }; + + log1p = function log1p(n: number): number { + return Math.log(1 + n); + }; + + sign = function sign(n: number): number { + n = Number(n); + if (n === 0 || n !== n) { + return n; + } + return n > 0 ? 1 : -1; + }; + + sinh = function sinh(n: number): number { + const m = Math.exp(n); + return (m - 1 / m) / 2; + }; + + tanh = function tanh(n: number): number { + if (n === Infinity) { + return 1; + } + else if (n === -Infinity) { + return -1; + } + else { + const y = Math.exp(2 * n); + return (y - 1) / (y + 1); + } + }; + + trunc = function trunc(n: number): number { + return n < 0 ? Math.ceil(n) : Math.floor(n); + }; +} + +if (!has('es6-math-imul')) { + imul = function imul(n: number, m: number): number { + // See: http://mzl.la/1K279FK + const ah = (n >>> 16) & 0xffff; + const al = n & 0xffff; + const bh = (m >>> 16) & 0xffff; + const bl = m & 0xffff; + return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0); + }; +} diff --git a/src/native/Map.ts b/src/native/Map.ts deleted file mode 100644 index 200534a..0000000 --- a/src/native/Map.ts +++ /dev/null @@ -1,22 +0,0 @@ -import global from '../global'; - -export interface Map { - clear(): void; - delete(key: K): boolean; - forEach(callbackfn: (value: V, index: K, map: Map) => void, thisArg?: any): void; - get(key: K): V | undefined; - has(key: K): boolean; - set(key: K, value?: V): this; - readonly size: number; -} - -export interface MapConstructor { - new (): Map; - new (entries?: [K, V][]): Map; - prototype: Map; -} - -/* tslint:disable-next-line:variable-name */ -const Map: MapConstructor = global.Map; - -export default Map; diff --git a/src/native/Promise.ts b/src/native/Promise.ts deleted file mode 100644 index 2a1305b..0000000 --- a/src/native/Promise.ts +++ /dev/null @@ -1,6 +0,0 @@ -import global from '../global'; - -/* tslint:disable-next-line:variable-name */ -const PromiseConstructor = global.Promise; - -export default PromiseConstructor; diff --git a/src/native/Set.ts b/src/native/Set.ts deleted file mode 100644 index d98d313..0000000 --- a/src/native/Set.ts +++ /dev/null @@ -1,21 +0,0 @@ -import global from '../global'; - -export interface Set { - add(value: T): this; - clear(): void; - delete(value: T): boolean; - forEach(callbackfn: (value: T, index: T, set: Set) => void, thisArg?: any): void; - has(value: T): boolean; - readonly size: number; -} - -export interface SetConstructor { - new (): Set; - new (values?: T[]): Set; - prototype: Set; -} - -/* tslint:disable-next-line:variable-name */ -const Set: SetConstructor = global.Set; - -export default Set; diff --git a/src/native/Symbol.ts b/src/native/Symbol.ts deleted file mode 100644 index fb3a182..0000000 --- a/src/native/Symbol.ts +++ /dev/null @@ -1,21 +0,0 @@ -import global from '../global'; -import { getValueDescriptor } from '../support/util'; - -/* tslint:disable-next-line:variable-name */ -const SymbolShim: SymbolConstructor = global.Symbol; - -/** - * Fill any missing well known symbols if the native Symbol is missing them - */ -[ 'hasInstance', 'isConcatSpreadable', 'iterator', 'species', 'replace', 'search', 'split', 'match', 'toPrimitive', - 'toStringTag', 'unscopables' ].forEach((wellKnown) => { - if (!( Symbol)[wellKnown]) { - Object.defineProperty(Symbol, wellKnown, getValueDescriptor(Symbol.for(wellKnown), false, false)); - } - }); - -export function isSymbol(value: any): value is symbol { - return typeof value === 'symbol'; -}; - -export default SymbolShim; diff --git a/src/native/WeakMap.ts b/src/native/WeakMap.ts deleted file mode 100644 index 5383d69..0000000 --- a/src/native/WeakMap.ts +++ /dev/null @@ -1,20 +0,0 @@ -import global from '../global'; - -export interface WeakMap { - clear(): void; - delete(key: K): boolean; - get(key: K): V; - has(key: K): boolean; - set(key: K, value?: V): this; -} - -export interface WeakMapConstructor { - new (): WeakMap; - new (entries?: [K, V][]): WeakMap; - prototype: WeakMap; -} - -/* tslint:disable-next-line:variable-name */ -const WeakMap: WeakMapConstructor = global.WeakMap; - -export default WeakMap; diff --git a/src/native/array.ts b/src/native/array.ts deleted file mode 100644 index c08ab54..0000000 --- a/src/native/array.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { wrapNative } from '../support/util'; -import { Iterable } from './iterator'; - -export interface MapCallback { - /** - * A callback function when mapping - * - * @param element The element that is currently being mapped - * @param index The current index of the element - */ - (element: T, index: number): T; -} - -export interface FindCallback { - /** - * A callback function when using find - * - * @param element The element that is currenty being analysed - * @param index The current index of the element that is being analysed - * @param array The source array - */ - (element: T, index: number, array: ArrayLike): boolean; -} - -/* ES6 Array static methods */ - -export interface From { - (arrayLike: string, mapFunction?: MapCallback, thisArg?: {}): Array; - (arrayLike: Iterable | ArrayLike, mapFunction?: MapCallback, thisArg?: {}): Array; - /** - * The Array.from() method creates a new Array instance from an array-like or iterable object. - * - * @param arrayLike An array-like or iterable object to convert to an array - * @param mapFunction A map function to call on each element in the array - * @param thisArg The execution context for the map function - * @return The new Array - */ - (arrayLike: (string | Iterable | ArrayLike), mapFunction?: MapCallback, thisArg?: {}): Array; -} - -export const from: From = ( Array).from; - -/** - * Creates a new array from the function parameters. - * - * @param arguments Any number of arguments for the array - * @return An array from the given arguments - */ -export const of: (...items: any[]) => any[] = ( Array).of; - -/* ES6 Array instance methods */ - -/** - * Copies data internally within an array or array-like object. - * - * @param target The target array-like object - * @param offset The index to start copying values to; if negative, it counts backwards from length - * @param start The first (inclusive) index to copy; if negative, it counts backwards from length - * @param end The last (exclusive) index to copy; if negative, it counts backwards from length - * @return The target - */ -export const copyWithin: (target: ArrayLike, offset: number, start: number, end?: number) => ArrayLike = wrapNative(( Array.prototype).copyWithin); - -/** - * Fills elements of an array-like object with the specified value. - * - * @param target The target to fill - * @param value The value to fill each element of the target with - * @param start The first index to fill - * @param end The (exclusive) index at which to stop filling - * @return The filled target - */ -export const fill: (target: ArrayLike, value: any, start?: number, end?: number) => ArrayLike = wrapNative(( Array.prototype).fill); - -/** - * Finds and returns the first instance matching the callback or undefined if one is not found. - * - * @param target An array-like object - * @param callback A function returning if the current value matches a criteria - * @param [thisArg] The execution context for the find function - * @return The first element matching the callback, or undefined if one does not exist - */ -export const find: (target: ArrayLike, callback: FindCallback, thisArg?: {}) => T = wrapNative(( Array.prototype).find); - -/** - * Performs a linear search and returns the first index whose value satisfies the passed callback, - * or -1 if no values satisfy it. - * - * @param target An array-like object - * @param callback A function returning true if the current value satisfies its criteria - * @param [thisArg] The execution context for the find function - * @return The first index whose value satisfies the passed callback, or -1 if no values satisfy it - */ -export const findIndex: (target: ArrayLike, callback: FindCallback, thisArg?: {}) => number = wrapNative(( Array.prototype).findIndex); - -/* ES7 Array instance methods */ - -/** - * Determines whether an array includes a given value - * - * @param target the target array-like object - * @param searchElement the item to search for - * @param fromIndex the starting index to search from - */ -export const includes: (target: ArrayLike, searchElement: T, fromIndex?: number) => boolean = wrapNative(( Array.prototype).includes); diff --git a/src/native/iterator.ts b/src/native/iterator.ts deleted file mode 100644 index 333c700..0000000 --- a/src/native/iterator.ts +++ /dev/null @@ -1,92 +0,0 @@ -export interface IteratorResult { - readonly done: boolean; - readonly value?: T; -} - -export interface Iterator { - next(value?: any): IteratorResult; - return?(value?: any): IteratorResult; - throw?(e?: any): IteratorResult; -} - -export interface Iterable { - [Symbol.iterator](): Iterator; -} - -export interface IterableIterator extends Iterator { - [Symbol.iterator](): IterableIterator; -} - -/** - * A type guard for checking if something has an Iterable interface - * @param value The value to type guard against - */ -export function isIterable(value: any): value is Iterable { - return value && typeof value[Symbol.iterator] !== 'undefined'; -} - -/** - * A type guard for checking if something is ArrayLike - * @param value The value to type guard against - */ -export function isArrayLike(value: any): value is ArrayLike { - return value && typeof value.length !== 'undefined'; -} - -/** - * Returns the iterator for an object - * - * @param iterable The iterable object to return the iterator for - */ -export function get(iterable: Iterable | ArrayLike | string): Iterator | undefined { - if (isIterable(iterable)) { - /* have to cast as any, because the assumed index is implicit any */ - return ( iterable)[Symbol.iterator](); - } -}; - -export interface ForOfCallback { - /** - * A callback function for a forOf() iteration - * - * @param value The current value - * @param object The object being iterated over - * @param doBreak A function, if called, will stop the iteration - */ - (value: T, object: Iterable | ArrayLike | string, doBreak: () => void): void; -} - -/** - * Shims the functionality of `for ... of` blocks - * - * @param iterable The object the provides an interator interface - * @param callback The callback which will be called for each item of the iterable - * @param thisArg Optional scope to pass the callback - */ -export function forOf(iterable: Iterable | ArrayLike | string, callback: ForOfCallback, thisArg?: any): void { - let broken = false; - - function doBreak() { - broken = true; - } - - const iterator = get(iterable); - if (!iterator) { - throw new TypeError('Cannot resolve iterator interface.'); - } - let result = iterator.next(); - - /** - * TypeScript when targetting ES5 will destructure `foo ... of` only for array like objects - * using a `for` loop. This obviously causes problems when trying to support iterator - * functionality and would not make the code transparent between ES5 and ES6, therefore - * creating our own iterator loop that would work seemlessly, irrespective of the target - */ - while (!result.done) { - callback.call(thisArg, result.value, iterable, doBreak); - if (broken) { - return; - } - result = iterator.next(); - } -} diff --git a/src/native/main.ts b/src/native/main.ts deleted file mode 100644 index bec7d24..0000000 --- a/src/native/main.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as array from './array'; -import * as iterator from './iterator'; -import Map from './Map'; -import * as math from './math'; -import * as number from './number'; -import * as object from './object'; -import Promise from './Promise'; -import Set from './Set'; -import * as string from './string'; -import Symbol from './Symbol'; -import WeakMap from './WeakMap'; - -export { - array, - iterator, - Map, - math, - number, - object, - Promise, - Set, - string, - Symbol, - WeakMap -}; diff --git a/src/native/math.ts b/src/native/math.ts deleted file mode 100644 index 7830821..0000000 --- a/src/native/math.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Returns the hyperbolic arccosine of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const acosh: (n: number) => number = ( Math).acosh; - -/** - * Returns the hyperbolic arcsine of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const asinh: (n: number) => number = ( Math).asinh; - -/** - * Returns the hyperbolic arctangent of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const atanh: (n: number) => number = ( Math).atanh; - -/** - * Returns the cube root of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const cbrt: (n: number) => number = ( Math).cbrt; - -/** - * Returns the number of leading zero bits in the 32-bit - * binary representation of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const clz32: (n: number) => number = ( Math).clz32; - -/** - * Returns the hyperbolic cosine of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const cosh: (n: number) => number = ( Math).cosh; - -/** - * Returns e raised to the specified power minus one. - * - * @param n The number to use in calculation - * @return The result - */ -export const expm1: (n: number) => number = ( Math).expm1; - -/** - * Returns the nearest single-precision float representation of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const fround: (n: number) => number = ( Math).fround; - -/** - * Returns the square root of the sum of squares of its arguments. - * - * @return The result - */ -export const hypot: (...args: number[]) => number = ( Math).hypot; - -/** - * Returns the result of the 32-bit multiplication of the two parameters. - * - * @param n The number to use in calculation - * @param m The number to use in calculation - * @return The result - */ -export const imul: (n: number, m: number) => number = ( Math).imul; - -/** - * Returns the base 2 logarithm of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const log2: (n: number) => number = ( Math).log2; - -/** - * Returns the base 10 logarithm of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const log10: (n: number) => number = ( Math).log10; - -/** - * Returns the natural logarithm of 1 + a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const log1p: (n: number) => number = ( Math).log1p; - -/** - * Returns the sign of a number, indicating whether the number is positive. - * - * @param n The number to use in calculation - * @return 1 if the number is positive, -1 if the number is negative, or 0 if the number is 0 - */ -export const sign: (n: number) => number = ( Math).sign; - -/** - * Returns the hyperbolic sine of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const sinh: (n: number) => number = ( Math).sinh; - -/** - * Returns the hyperbolic tangent of a number. - * - * @param n The number to use in calculation - * @return The result - */ -export const tanh: (n: number) => number = ( Math).tanh; - -/** - * Returns the integral part of a number by removing any fractional digits. - * - * @param n The number to use in calculation - * @return The result - */ -export const trunc: (n: number) => number = ( Math).trunc; diff --git a/src/native/number.ts b/src/native/number.ts deleted file mode 100644 index e5abfb2..0000000 --- a/src/native/number.ts +++ /dev/null @@ -1,52 +0,0 @@ -import global from '../global'; - -/** - * The smallest interval between two representable numbers. - */ -export const EPSILON = global.Number.EPSILON; - -/** - * The maximum safe integer in JavaScript - */ -export const MAX_SAFE_INTEGER = global.Number.MAX_SAFE_INTEGER; - -/** - * The minimum safe integer in JavaScript - */ -export const MIN_SAFE_INTEGER = global.Number.MIN_SAFE_INTEGER; - -/** - * Determines whether the passed value is NaN without coersion. - * - * @param value The value to test - * @return true if the value is NaN, false if it is not - */ -export const isNaN = global.Number.isNaN; - -/** - * Determines whether the passed value is a finite number without coersion. - * - * @param value The value to test - * @return true if the value is finite, false if it is not - */ -export const isFinite = global.Number.isFinite; - -/** - * Determines whether the passed value is an integer. - * - * @param value The value to test - * @return true if the value is an integer, false if it is not - */ -export const isInteger = global.Number.isInteger; - -/** - * Determines whether the passed value is an integer that is 'safe,' meaning: - * 1. it can be expressed as an IEEE-754 double precision number - * 2. it has a one-to-one mapping to a mathematical integer, meaning its - * IEEE-754 representation cannot be the result of rounding any other - * integer to fit the IEEE-754 representation - * - * @param value The value to test - * @return true if the value is an integer, false if it is not - */ -export const isSafeInteger = global.Number.isSafeInteger; diff --git a/src/native/object.ts b/src/native/object.ts deleted file mode 100644 index 0dbddd2..0000000 --- a/src/native/object.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Determines whether two values are the same value. - * - * @param value1 The first value to compare - * @param value2 The second value to compare - * @return true if the values are the same; false otherwise - */ -export const is: (value1: any, value2: any) => boolean = ( Object).is; - -/** - * Returns an array of own properties who key is a symbol - * - * @param o The object to return the properties for - */ -export const getOwnPropertySymbols: (o: any) => symbol[] = ( Object).getOwnPropertySymbols; - -/** - * Returns an array of own properties who key is a string - * - * @param o The object to return the properties for - */ -export const getOwnPropertyNames: (o: any) => string[] = Object.getOwnPropertyNames; - -/** - * Returns the names of the enumerable properties and methods of an object. - * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. - */ -export const keys: (o: any) => string[] = Object.keys; diff --git a/src/native/string.ts b/src/native/string.ts deleted file mode 100644 index 4da2972..0000000 --- a/src/native/string.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { wrapNative } from '../support/util'; - -/** - * The minimum location of high surrogates - */ -export const HIGH_SURROGATE_MIN = 0xD800; - -/** - * The maximum location of high surrogates - */ -export const HIGH_SURROGATE_MAX = 0xDBFF; - -/** - * The minimum location of low surrogates - */ -export const LOW_SURROGATE_MIN = 0xDC00; - -/** - * The maximum location of low surrogates - */ -export const LOW_SURROGATE_MAX = 0xDFFF; - -/** - * A tag function for template strings to get the template string's raw string form. - * - * @param callSite Call site object (or a template string in TypeScript, which will transpile to one) - * @param substitutions Values to substitute within the template string (TypeScript will generate these automatically) - * @return String containing the raw template string with variables substituted - * - * @example - * // Within TypeScript; logs 'The answer is:\\n42' - * let answer = 42; - * console.log(string.raw`The answer is:\n${answer}`); - * - * @example - * // The same example as above, but directly specifying a JavaScript object and substitution - * console.log(string.raw({ raw: [ 'The answer is:\\n', '' ] }, 42)); - */ -export const raw: (callSite: TemplateStringsArray, ...substitutions: any[]) => string = ( String).raw; - -/** - * Returns the UTF-16 encoded code point value of a given position in a string. - * - * @param text The string containing the element whose code point is to be determined - * @param position Position of an element within the string to retrieve the code point value from - * @return A non-negative integer representing the UTF-16 encoded code point value - */ -export const fromCodePoint: (...codePoints: number[]) => string = ( String).fromCodePoint; - -/** - * Returns the UTF-16 encoded code point value of a given position in a string. - * - * @param text The string containing the element whose code point is to be determined - * @param position Position of an element within the string to retrieve the code point value from - * @return A non-negative integer representing the UTF-16 encoded code point value - */ -export const codePointAt: (text: string, position?: number) => number = wrapNative(( String.prototype).codePointAt); - -/** - * Returns a string containing the given string repeated the specified number of times. - * - * @param text The string to repeat - * @param count The number of times to repeat the string - * @return A string containing the input string repeated count times - */ -export const repeat: (text: string, count?: number) => string = wrapNative(( String.prototype).repeat); - -/** - * Determines whether a string begins with the given substring (optionally starting from a given index). - * - * @param text The string to look for the search string within - * @param search The string to search for - * @param position The index to begin searching at - * @return Boolean indicating if the search string was found at the beginning of the given string - */ -export const startsWith: (text: string, search: string, position?: number) => boolean = wrapNative(( String.prototype).startsWith); - -/** - * Determines whether a string ends with the given substring. - * - * @param text The string to look for the search string within - * @param search The string to search for - * @param endPosition The index searching should stop before (defaults to text.length) - * @return Boolean indicating if the search string was found at the end of the given string - */ -export const endsWith: (text: string, search: string, endPosition?: number) => boolean = wrapNative(( String.prototype).endsWith); - -/** - * Determines whether a string includes the given substring (optionally starting from a given index). - * - * @param text The string to look for the search string within - * @param search The string to search for - * @param position The index to begin searching at - * @return Boolean indicating if the search string was found within the given string - */ -export const includes: (text: string, search: string, position?: number) => boolean = wrapNative(( String.prototype).includes); diff --git a/src/number.ts b/src/number.ts index e2378f7..2254f9e 100644 --- a/src/number.ts +++ b/src/number.ts @@ -31,7 +31,7 @@ export function isNaN(value: any): boolean { * @param value The value to test * @return true if the value is finite, false if it is not */ -export function isFinite(value: any): boolean { +export function isFinite(value: any): value is number { return typeof value === 'number' && global.isFinite(value); } @@ -41,7 +41,7 @@ export function isFinite(value: any): boolean { * @param value The value to test * @return true if the value is an integer, false if it is not */ -export function isInteger(value: any): boolean { +export function isInteger(value: any): value is number { return isFinite(value) && Math.floor(value) === value; } @@ -55,6 +55,6 @@ export function isInteger(value: any): boolean { * @param value The value to test * @return true if the value is an integer, false if it is not */ -export function isSafeInteger(value: any): boolean { +export function isSafeInteger(value: any): value is number { return isInteger(value) && Math.abs(value) <= MAX_SAFE_INTEGER; } diff --git a/src/object.ts b/src/object.ts index b318fed..bdf0b17 100644 --- a/src/object.ts +++ b/src/object.ts @@ -1,125 +1,197 @@ +import global from './global'; import has from './support/has'; import { isSymbol } from './Symbol'; -namespace Shim { - export function is(value1: any, value2: any): boolean { - if (value1 === value2) { - return value1 !== 0 || 1 / value1 === 1 / value2; // -0 - } - return value1 !== value1 && value2 !== value2; // NaN - } - - export function getOwnPropertySymbols(o: any): symbol[] { - return Object.getOwnPropertyNames(o).filter((key) => Boolean(key.match(/^@@.+/))) - .map((key) => Symbol.for(key.substring(2))); - } - - export function getOwnPropertyNames(o: any): string[] { - return Object.getOwnPropertyNames(o).filter((key) => !Boolean(key.match(/^@@.+/))); - } - - export function symbolAwareKeys(o: any): string[] { - return Object.keys(o).filter((key) => !Boolean(key.match(/^@@.+/))); - } +export interface ObjectAssign { + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param source The source object from which to copy properties. + */ + (target: T, source: U): T & U; + + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param source1 The first source object from which to copy properties. + * @param source2 The second source object from which to copy properties. + */ + (target: T, source1: U, source2: V): T & U & V; + + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param source1 The first source object from which to copy properties. + * @param source2 The second source object from which to copy properties. + * @param source3 The third source object from which to copy properties. + */ + (target: T, source1: U, source2: V, source3: W): T & U & V & W; + + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param sources One or more source objects from which to copy properties + */ + (target: object, ...sources: any[]): any; +} - export function getOwnPropertyDescriptor(o: any, prop: string | symbol): PropertyDescriptor | undefined { - if (isSymbol(prop)) { - return ( Object).getOwnPropertyDescriptor(o, prop); - } else { - return Object.getOwnPropertyDescriptor(o, prop); - } - } +export interface ObjectEnteries { + /** + * Returns an array of key/values of the enumerable properties of an object + * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. + */ + (o: T): [keyof T, T[K]][]; + + /** + * Returns an array of key/values of the enumerable properties of an object + * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. + */ + (o: object): [string, any][]; +} - export function values(o: any): any[] { - return keys(o).map(key => o[ key ]); - } +export interface ObjectGetOwnPropertyDescriptors { + (o: T): { [K in keyof T]: PropertyDescriptor; }; + (o: any): { [key: string]: PropertyDescriptor; }; +} - export function entries(o: any): any[] { - return keys(o).map(key => [ key, o[ key ] ]); - } +export interface ObjectValues { + /** + * Returns an array of values of the enumerable properties of an object + * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. + */ + (o: { [s: string]: T }): T[]; + + /** + * Returns an array of values of the enumerable properties of an object + * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. + */ + (o: object): any[]; } +export let assign: ObjectAssign; + /** - * Determines whether two values are the same value. - * - * @param value1 The first value to compare - * @param value2 The second value to compare - * @return true if the values are the same; false otherwise + * Gets the own property descriptor of the specified object. + * An own property descriptor is one that is defined directly on the object and is not + * inherited from the object's prototype. + * @param o Object that contains the property. + * @param p Name of the property. */ -export const is: (value1: any, value2: any) => boolean = 'is' in Object - ? ( Object).is - : Shim.is; +export let getOwnPropertyDescriptor: (o: T, propertyKey: K) => PropertyDescriptor | undefined; /** - * Detect if there is native support for Symbol properties in Object + * Returns the names of the own properties of an object. The own properties of an object are those that are defined directly + * on that object, and are not inherited from the object's prototype. The properties of an object include both fields (objects) and functions. + * @param o Object that contains the own properties. */ -const hasGetOwnPropertySymbols = has('es6-symbol') && 'getOwnPropertySymbols' in Object; +export let getOwnPropertyNames: (o: any) => string[]; /** - * Returns an array of own properties who key is a symbol - * - * @param o The object to return the properties for + * Returns an array of all symbol properties found directly on object o. + * @param o Object to retrieve the symbols from. */ -export const getOwnPropertySymbols: (o: any) => symbol[] = hasGetOwnPropertySymbols - ? ( Object).getOwnPropertySymbols - : Shim.getOwnPropertySymbols; +export let getOwnPropertySymbols: (o: any) => symbol[]; /** - * Returns an array of own properties who key is a string - * - * @param o The object to return the properties for + * Returns true if the values are the same value, false otherwise. + * @param value1 The first value. + * @param value2 The second value. */ -/* intentionally detecting `getOwnPropertySymbols` because we should should provide the shim - * when there is no support for symbols */ -export const getOwnPropertyNames: (o: any) => string[] = hasGetOwnPropertySymbols - ? Object.getOwnPropertyNames - : Shim.getOwnPropertyNames; +export let is: (value1: any, value2: any) => boolean; /** * Returns the names of the enumerable properties and methods of an object. * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. */ -/* intentionally detecting `getOwnPropertySymbols` because we should should provide the shim - * when there is no support for symbols */ -export const keys: (o: any) => string[] = hasGetOwnPropertySymbols - ? Object.keys - : Shim.symbolAwareKeys; +export let keys: (o: object) => string[]; -/** - * Returns the values of the enumerable properties and methods of an object. - * @param o Object that contains the properties and methods. - */ -export const values: (o: any) => any[] = 'values' in Object - ? ( Object).values - : Shim.values; +/* ES7 Object static methods */ -/** - * Returns the keys and values of the enumerable properties and methods of an object. Each entry will be returned as an - * array holding the [key, value]. - * @param o Object that contains the properties and methods. - */ -export const entries: (o: any) => any[] = 'entries' in Object - ? ( Object).entries - : Shim.entries; +export let getOwnPropertyDescriptors: ObjectGetOwnPropertyDescriptors; -export const getOwnPropertyDescriptor: (o: any, property: string | symbol) => PropertyDescriptor | undefined = hasGetOwnPropertySymbols - ? Object.getOwnPropertyDescriptor - : Shim.getOwnPropertyDescriptor; +export let entries: ObjectEnteries; -function getOwnPropertyDescriptorsWrapper(o: any): any { - let descriptors: {[_: string]: PropertyDescriptor} = getOwnPropertyNames(o).reduce((descriptors: {[_: string]: PropertyDescriptor}, key: string) => { - descriptors[ key ] = getOwnPropertyDescriptor(o, key); - return descriptors; - }, {}); +export let values: ObjectValues; - getOwnPropertySymbols(o).forEach((sym: symbol) => { - descriptors[ sym ] = getOwnPropertyDescriptor(o, sym); - }); +if (has('es6-object')) { + const globalObject = global.Object; + assign = globalObject.assign; + getOwnPropertyDescriptor = globalObject.getOwnPropertyDescriptor; + getOwnPropertyNames = globalObject.getOwnPropertyNames; + getOwnPropertySymbols = globalObject.getOwnPropertySymbols; + is = globalObject.is; + keys = globalObject.keys; +} +else { + keys = function symbolAwareKeys(o: object): string[] { + return Object.keys(o).filter((key) => !Boolean(key.match(/^@@.+/))); + }; + + assign = function assign(target: any, ...sources: any[]) { + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + const to = Object(target); + sources.forEach((nextSource) => { + if (nextSource) { // Skip over if undefined or null + keys(nextSource).forEach((nextKey) => { + to[nextKey] = nextSource[nextKey]; + }); + } + }); + + return to; + }; + + getOwnPropertyDescriptor = function getOwnPropertyDescriptor(o: any, prop: string | symbol): PropertyDescriptor | undefined { + if (isSymbol(prop)) { + return ( Object).getOwnPropertyDescriptor(o, prop); + } else { + return Object.getOwnPropertyDescriptor(o, prop); + } + }; - return descriptors; + getOwnPropertyNames = function getOwnPropertyNames(o: any): string[] { + return Object.getOwnPropertyNames(o).filter((key) => !Boolean(key.match(/^@@.+/))); + }; + + getOwnPropertySymbols = function getOwnPropertySymbols(o: any): symbol[] { + return Object.getOwnPropertyNames(o).filter((key) => Boolean(key.match(/^@@.+/))) + .map((key) => Symbol.for(key.substring(2))); + }; + + is = function is(value1: any, value2: any): boolean { + if (value1 === value2) { + return value1 !== 0 || 1 / value1 === 1 / value2; // -0 + } + return value1 !== value1 && value2 !== value2; // NaN + }; } -/* Return descriptors for enumerable and non enumerable properties on an object */ -export const getOwnPropertyDescriptors: (o: any) => any = 'getOwnPropertyDescriptors' in Object - ? ( Object).getOwnPropertyDescriptors - : getOwnPropertyDescriptorsWrapper; +if (has('es2017-object')) { + const globalObject = global.Object; + getOwnPropertyDescriptors = globalObject.getOwnPropertyDescriptors; + entries = globalObject.entries; + values = globalObject.values; +} +else { + getOwnPropertyDescriptors = function getOwnPropertyDescriptors(o: any) { + return getOwnPropertyNames(o).reduce((previous, key) => { + previous[key] = getOwnPropertyDescriptor(o, key)!; + return previous; + }, {} as { [key: string]: PropertyDescriptor }); + }; + + entries = function entries(o: any): [string, any][] { + return keys(o).map((key) => [ key, o[key] ] as [string, any]); + }; + + values = function values(o: any): any[] { + return keys(o).map((key) => o[ key ]); + }; +} diff --git a/src/string.ts b/src/string.ts index 2220241..122449b 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,6 +1,27 @@ +import global from './global'; import has from './support/has'; import { wrapNative } from './support/util'; +export interface StringNormalize { + /** + * Returns the String value result of normalizing the string into the normalization form + * named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms. + * @param target The target string + * @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default + * is "NFC" + */ + (target: string, form: 'NFC' | 'NFD' | 'NFKC' | 'NFKD'): string; + + /** + * Returns the String value result of normalizing the string into the normalization form + * named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms. + * @param target The target string + * @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default + * is "NFC" + */ + (target: string, form?: string): string; +} + /** * The minimum location of high surrogates */ @@ -21,14 +42,124 @@ export const LOW_SURROGATE_MIN = 0xDC00; */ export const LOW_SURROGATE_MAX = 0xDFFF; -export namespace Shim { +/* ES6 static methods */ + +/** + * Return the String value whose elements are, in order, the elements in the List elements. + * If length is 0, the empty string is returned. + * @param codePoints The code points to generate the string + */ +export let fromCodePoint: (...codePoints: number[]) => string; + +/** + * `raw` is intended for use as a tag function of a Tagged Template String. When called + * as such the first argument will be a well formed template call site object and the rest + * parameter will contain the substitution values. + * @param template A well-formed template string call site representation. + * @param substitutions A set of substitution values. + */ +export let raw: (template: TemplateStringsArray, ...substitutions: any[]) => string; + +/* ES6 instance methods */ + +/** + * Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point + * value of the UTF-16 encoded code point starting at the string element at position pos in + * the String resulting from converting this object to a String. + * If there is no element at that position, the result is undefined. + * If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos. + */ +export let codePointAt: (target: string, pos?: number) => number | undefined; + +/** + * Returns true if the sequence of elements of searchString converted to a String is the + * same as the corresponding elements of this object (converted to a String) starting at + * endPosition – length(this). Otherwise returns false. + */ +export let endsWith: (target: string, searchString: string, endPosition?: number) => boolean; + +/** + * Returns true if searchString appears as a substring of the result of converting this + * object to a String, at one or more positions that are + * greater than or equal to position; otherwise, returns false. + * @param target The target string + * @param searchString search string + * @param position If position is undefined, 0 is assumed, so as to search all of the String. + */ +export let includes: (target: string, searchString: string, position?: number) => boolean; + +/** + * Returns the String value result of normalizing the string into the normalization form + * named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms. + * @param target The target string + * @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default + * is "NFC" + */ +export let normalize: StringNormalize; + +/** + * Returns a String value that is made from count copies appended together. If count is 0, + * T is the empty String is returned. + * @param count number of copies to append + */ +export let repeat: (target: string, count?: number) => string; + +/** + * Returns true if the sequence of elements of searchString converted to a String is the + * same as the corresponding elements of this object (converted to a String) starting at + * position. Otherwise returns false. + */ +export let startsWith: (target: string, searchString: string, position?: number) => boolean; + +/* ES7 instance methods */ + +/** + * Pads the current string with a given string (possibly repeated) so that the resulting string reaches a given length. + * The padding is applied from the end (right) of the current string. + * + * @param target The target string + * @param maxLength The length of the resulting string once the current string has been padded. + * If this parameter is smaller than the current string's length, the current string will be returned as it is. + * + * @param fillString The string to pad the current string with. + * If this string is too long, it will be truncated and the left-most part will be applied. + * The default value for this parameter is " " (U+0020). + */ +export let padEnd: (target: string, maxLength: number, fillString?: string) => string; + +/** + * Pads the current string with a given string (possibly repeated) so that the resulting string reaches a given length. + * The padding is applied from the start (left) of the current string. + * + * @param target The target string + * @param maxLength The length of the resulting string once the current string has been padded. + * If this parameter is smaller than the current string's length, the current string will be returned as it is. + * + * @param fillString The string to pad the current string with. + * If this string is too long, it will be truncated and the left-most part will be applied. + * The default value for this parameter is " " (U+0020). + */ +export let padStart: (target: string, maxLength: number, fillString?: string) => string; + +if (has('es6-string') && has('es6-string-raw')) { + fromCodePoint = global.String.fromCodePoint; + raw = global.String.raw; + + codePointAt = wrapNative(global.String.prototype.codePointAt); + endsWith = wrapNative(global.String.prototype.endsWith); + includes = wrapNative(global.String.prototype.includes); + normalize = wrapNative(global.String.prototype.normalize); + repeat = wrapNative(global.String.prototype.repeat); + startsWith = wrapNative(global.String.prototype.startsWith); +} +else { /** * Validates that text is defined, and normalizes position (based on the given default if the input is NaN). * Used by startsWith, includes, and endsWith. * * @return Normalized position. */ - function normalizeSubstringArgs(name: string, text: string, search: string, position: number, + const normalizeSubstringArgs = function(name: string, text: string, search: string, position: number, isEnd: boolean = false): [ string, string, number ] { if (text == null) { throw new TypeError('string.' + name + ' requires a valid string to search against.'); @@ -37,25 +168,9 @@ export namespace Shim { const length = text.length; position = position !== position ? (isEnd ? length : 0) : position; return [ text, String(search), Math.min(Math.max(position, 0), length) ]; - } - - export function raw(callSite: TemplateStringsArray, ...substitutions: any[]): string { - let rawStrings = callSite.raw; - let result = ''; - let numSubstitutions = substitutions.length; - - if (callSite == null || callSite.raw == null) { - throw new TypeError('string.raw requires a valid callSite object with a raw value'); - } - - for (let i = 0, length = rawStrings.length; i < length; i++) { - result += rawStrings[i] + (i < numSubstitutions && i < length - 1 ? substitutions[i] : ''); - } - - return result; - } + }; - export function fromCodePoint(...codePoints: number[]): string { + fromCodePoint = function fromCodePoint(...codePoints: number[]): string { // Adapted from https://github.com/mathiasbynens/String.fromCodePoint const length = arguments.length; if (!length) { @@ -97,9 +212,25 @@ export namespace Shim { } } return result; - } + }; + + raw = function raw(callSite: TemplateStringsArray, ...substitutions: any[]): string { + let rawStrings = callSite.raw; + let result = ''; + let numSubstitutions = substitutions.length; + + if (callSite == null || callSite.raw == null) { + throw new TypeError('string.raw requires a valid callSite object with a raw value'); + } + + for (let i = 0, length = rawStrings.length; i < length; i++) { + result += rawStrings[i] + (i < numSubstitutions && i < length - 1 ? substitutions[i] : ''); + } + + return result; + }; - export function codePointAt(text: string, position: number = 0): number | undefined { + codePointAt = function codePointAt(text: string, position: number = 0): number | undefined { // Adapted from https://github.com/mathiasbynens/String.prototype.codePointAt if (text == null) { throw new TypeError('string.codePointAt requries a valid string.'); @@ -124,11 +255,29 @@ export namespace Shim { } } return first; - } + }; + + endsWith = function endsWith(text: string, search: string, endPosition?: number): boolean { + if (endPosition == null) { + endPosition = text.length; + } + + [ text, search, endPosition ] = normalizeSubstringArgs('endsWith', text, search, endPosition, true); - /* TODO: Missing normalize */ + const start = endPosition - search.length; + if (start < 0) { + return false; + } - export function repeat(text: string, count: number = 0): string { + return text.slice(start, endPosition) === search; + }; + + includes = function includes(text: string, search: string, position: number = 0): boolean { + [ text, search, position ] = normalizeSubstringArgs('includes', text, search, position); + return text.indexOf(search, position) !== -1; + }; + + repeat = function repeat(text: string, count: number = 0): string { // Adapted from https://github.com/mathiasbynens/String.prototype.repeat if (text == null) { throw new TypeError('string.repeat requires a valid string.'); @@ -151,9 +300,9 @@ export namespace Shim { count >>= 1; } return result; - } + }; - export function startsWith(text: string, search: string, position: number = 0): boolean { + startsWith = function startsWith(text: string, search: string, position: number = 0): boolean { search = String(search); [ text, search, position ] = normalizeSubstringArgs('startsWith', text, search, position); @@ -163,29 +312,15 @@ export namespace Shim { } return text.slice(position, end) === search; - } - - export function endsWith(text: string, search: string, endPosition?: number): boolean { - if (endPosition == null) { - endPosition = text.length; - } - - [ text, search, endPosition ] = normalizeSubstringArgs('endsWith', text, search, endPosition, true); - - const start = endPosition - search.length; - if (start < 0) { - return false; - } - - return text.slice(start, endPosition) === search; - } - - export function includes(text: string, search: string, position: number = 0): boolean { - [ text, search, position ] = normalizeSubstringArgs('includes', text, search, position); - return text.indexOf(search, position) !== -1; - } + }; +} - export function padEnd(text: string, maxLength: number, fillString: string = ' '): string { +if (has('es2017-string')) { + padEnd = wrapNative(global.String.prototype.padEnd); + padStart = wrapNative(global.String.prototype.padStart); +} +else { + padEnd = function padEnd(text: string, maxLength: number, fillString: string = ' '): string { if (text === null || text === undefined) { throw new TypeError('string.repeat requires a valid string.'); } @@ -206,9 +341,9 @@ export namespace Shim { } return strText; - } + }; - export function padStart(text: string, maxLength: number, fillString: string = ' '): string { + padStart = function padStart(text: string, maxLength: number, fillString: string = ' '): string { if (text === null || text === undefined) { throw new TypeError('string.repeat requires a valid string.'); } @@ -229,122 +364,5 @@ export namespace Shim { } return strText; - } - - /* TODO: Provide an iterator for a string to mimic [Symbol.iterator]? */ + }; } - -/** - * A tag function for template strings to get the template string's raw string form. - * - * @param callSite Call site object (or a template string in TypeScript, which will transpile to one) - * @param substitutions Values to substitute within the template string (TypeScript will generate these automatically) - * @return String containing the raw template string with variables substituted - * - * @example - * // Within TypeScript; logs 'The answer is:\\n42' - * let answer = 42; - * console.log(string.raw`The answer is:\n${answer}`); - * - * @example - * // The same example as above, but directly specifying a JavaScript object and substitution - * console.log(string.raw({ raw: [ 'The answer is:\\n', '' ] }, 42)); - */ -export const raw: (callSite: TemplateStringsArray, ...substitutions: any[]) => string = has('es6-string-raw') - ? ( String).raw - : Shim.raw; - -/** - * Returns the UTF-16 encoded code point value of a given position in a string. - * - * @param text The string containing the element whose code point is to be determined - * @param position Position of an element within the string to retrieve the code point value from - * @return A non-negative integer representing the UTF-16 encoded code point value - */ -export const fromCodePoint: (...codePoints: number[]) => string = has('es6-string-fromcodepoint') - ? ( String).fromCodePoint - : Shim.fromCodePoint; - -/** - * Returns the UTF-16 encoded code point value of a given position in a string. - * - * @param text The string containing the element whose code point is to be determined - * @param position Position of an element within the string to retrieve the code point value from - * @return A non-negative integer representing the UTF-16 encoded code point value - */ -export const codePointAt: (text: string, position?: number) => number = has('es6-string-codepointat') - ? wrapNative(( String.prototype).codePointAt) - : Shim.codePointAt; - -/** - * Returns a string containing the given string repeated the specified number of times. - * - * @param text The string to repeat - * @param count The number of times to repeat the string - * @return A string containing the input string repeated count times - */ -export const repeat: (text: string, count?: number) => string = has('es6-string-repeat') - ? wrapNative(( String.prototype).repeat) - : Shim.repeat; - -/** - * Determines whether a string begins with the given substring (optionally starting from a given index). - * - * @param text The string to look for the search string within - * @param search The string to search for - * @param position The index to begin searching at - * @return Boolean indicating if the search string was found at the beginning of the given string - */ -export const startsWith: (text: string, search: string, position?: number) => boolean = has('es6-string-startswith') - ? wrapNative(( String.prototype).startsWith) - : Shim.startsWith; - -/** - * Determines whether a string ends with the given substring. - * - * @param text The string to look for the search string within - * @param search The string to search for - * @param endPosition The index searching should stop before (defaults to text.length) - * @return Boolean indicating if the search string was found at the end of the given string - */ -export const endsWith: (text: string, search: string, endPosition?: number) => boolean = has('es6-string-endswith') - ? wrapNative(( String.prototype).endsWith) - : Shim.endsWith; - -/** - * Determines whether a string includes the given substring (optionally starting from a given index). - * - * @param text The string to look for the search string within - * @param search The string to search for - * @param position The index to begin searching at - * @return Boolean indicating if the search string was found within the given string - */ -export const includes: (text: string, search: string, position?: number) => boolean = has('es6-string-includes') - ? wrapNative(( String.prototype).includes) - : Shim.includes; - -/** - * Pads the beginning of a string with a fill string until the string is a certain length. - * - * @param text The string to pad - * @param maxLength The desired length of the string - * @param fillString The string to be repeated (fully or partially) until text is the maximum length - * - * @return A string that is at least the maximum length specified, padded in the front if necessary. - */ -export const padStart: (text: string, maxLength: number, fillString?: string) => string = has('es6-string-padstart') - ? wrapNative(( String.prototype).padStart) - : Shim.padStart; - -/** - * Pads the end of a string with a fill string until the string is a certain length. - * - * @param text The string to pad - * @param maxLength The desired length of the string - * @param fillString The string to be repeated (fully or partially) until text is the maximum length - * - * @return A string that is at least the maximum length specified, padded at the end if necessary. - */ -export const padEnd: (text: string, maxLength: number, fillString?: string) => string = has('es6-string-padend') - ? wrapNative(( String.prototype).padEnd) - : Shim.padEnd; diff --git a/src/support/has.ts b/src/support/has.ts index b6195b9..0293bf8 100644 --- a/src/support/has.ts +++ b/src/support/has.ts @@ -1,28 +1,23 @@ +import has, { add } from '@dojo/has/has'; import global from '../global'; -import has from '@dojo/has/has'; -import { add } from '@dojo/has/has'; export default has; export * from '@dojo/has/has'; /* ECMAScript 6 and 7 Features */ -/* - * Determine whether or not native Symbol exists. If it doesn't, we don't want to use - * a few other native implementations like Map, WeakMap, Set. Consider a polyfill that provides Symbol, - * Map, etc in the global namespace. If the polyfill's Symbol is not compatible with our Symbol, neither - * will be anything that uses their iterator symbol, like Map, Set, etc. - */ - -/* Symbol */ -add('es6-symbol', typeof global.Symbol !== 'undefined' && typeof Symbol() === 'symbol'); - -/* Object */ -add('es6-object-assign', typeof ( Object).assign === 'function'); - /* Array */ -add('es6-array-from', 'from' in global.Array); -add('es6-array-of', 'of' in global.Array); +add('es6-array', () => { + return [ + 'from', + 'of' + ].every((key) => key in global.Array) && [ + 'findIndex', + 'find', + 'copyWithin' + ].every((key) => key in global.Array.prototype); +}); + add('es6-array-fill', () => { if ('fill' in global.Array.prototype) { /* Some versions of Safari do not properly implement this */ @@ -30,43 +25,55 @@ add('es6-array-fill', () => { } return false; }); -add('es6-array-findindex', 'findIndex' in global.Array.prototype); -add('es6-array-find', 'find' in global.Array.prototype); -add('es6-array-copywithin', 'copyWithin' in global.Array.prototype); -add('es7-array-includes', 'includes' in global.Array.prototype); -/* String */ -add('es6-string-raw', function () { - function getCallSite(callSite: TemplateStringsArray, ...substitutions: any[]) { - return callSite; - } +add('es7-array', () => 'includes' in global.Array.prototype); - if ('raw' in global.String) { - let b = 1; - let callSite = getCallSite`a\n${b}`; - - ( callSite).raw = [ 'a\\n' ]; - const supportsTrunc = global.String.raw(callSite, 42) === 'a:\\n'; +/* Map */ +add('es6-map', () => { + if (typeof global.Map === 'function') { + /* + IE11 and older versions of Safari are missing critical ES6 Map functionality + We wrap this in a try/catch because sometimes the Map constructor exists, but does not + take arguments (iOS 8.4) + */ + try { + const map = new global.Map([ [0, 1] ]); - return supportsTrunc; + return map.has(0) && + typeof map.keys === 'function' && has('es6-symbol') && + typeof map.values === 'function' && + typeof map.entries === 'function'; + } + catch (e) { + /* istanbul ignore next: not testing on iOS at the moment */ + return false; + } } - return false; }); -add('es6-string-fromcodepoint', 'fromCodePoint' in global.String); -add('es6-string-codepointat', 'codePointAt' in global.String.prototype); -add('es6-string-normalize', 'normalize' in global.String.prototype); -add('es6-string-repeat', 'repeat' in global.String.prototype); -add('es6-string-startswith', 'startsWith' in global.String.prototype); -add('es6-string-endswith', 'endsWith' in global.String.prototype); -add('es6-string-includes', 'includes' in global.String.prototype); -add('es6-string-padstart', 'padStart' in global.String.prototype); -add('es6-string-padend', 'padEnd' in global.String.prototype); /* Math */ +add('es6-math', () => { + return [ + 'clz32', + 'sign', + 'log10', + 'log2', + 'log1p', + 'expm1', + 'cosh', + 'sinh', + 'tanh', + 'acosh', + 'asinh', + 'atanh', + 'trunc', + 'fround', + 'cbrt', + 'hypot' + ].every((name) => typeof global.Math[name] === 'function'); +}); -add('es6-math-acosh', typeof global.Math.acosh === 'function'); -add('es6-math-clz32', typeof global.Math.clz32 === 'function'); add('es6-math-imul', () => { if ('imul' in global.Math) { /* Some versions of Safari on ios do not properly implement this */ @@ -75,11 +82,29 @@ add('es6-math-imul', () => { return false; }); -/* Promise */ -add('es6-promise', typeof global.Promise !== 'undefined' && has('es6-symbol')); +/* Object */ +add('es6-object', () => { + return has('es6-symbol') && [ + 'assign', + 'is', + 'getOwnPropertySymbols', + 'setPrototypeOf' + ].every((name) => typeof global.Object[name] === 'function'); +}); + +add('es2017-object', () => { + return [ + 'values', + 'entries', + 'getOwnPropertyDescriptors' + ].every((name) => typeof global.Object[name] === 'function'); +}); /* Observable */ -add('es-observable', typeof global.Observable !== 'undefined'); +add('es-observable', () => typeof global.Observable !== 'undefined'); + +/* Promise */ +add('es6-promise', () => typeof global.Promise !== 'undefined' && has('es6-symbol')); /* Set */ add('es6-set', () => { @@ -91,32 +116,50 @@ add('es6-set', () => { return false; }); -/* Map */ -add('es6-map', function () { - if (typeof global.Map === 'function') { - /* - IE11 and older versions of Safari are missing critical ES6 Map functionality - We wrap this in a try/catch because sometimes the Map constructor exists, but does not - take arguments (iOS 8.4) - */ - try { - const map = new global.Map([ [0, 1] ]); +/* String */ +add('es6-string', () => { + return [ /* static methods */ + 'fromCodePoint' + ].every((key) => typeof global.String[key] === 'function') && [ /* instance methods */ + 'codePointAt', + 'normalize', + 'repeat', + 'startsWith', + 'endsWith', + 'includes' + ].every((key) => typeof global.String.prototype[key] === 'function'); +}); - return map.has(0) && - typeof map.keys === 'function' && has('es6-symbol') && - typeof map.values === 'function' && - typeof map.entries === 'function'; - } - catch (e) { - /* istanbul ignore next: not testing on iOS at the moment */ - return false; - } +add('es6-string-raw', () => { + function getCallSite(callSite: TemplateStringsArray, ...substitutions: any[]) { + return callSite; } + + if ('raw' in global.String) { + let b = 1; + let callSite = getCallSite`a\n${b}`; + + ( callSite).raw = [ 'a\\n' ]; + const supportsTrunc = global.String.raw(callSite, 42) === 'a:\\n'; + + return supportsTrunc; + } + return false; }); +add('es2017-string', () => { + return [ + 'padStart', + 'padEnd' + ].every((key) => typeof global.String.prototype[key] === 'function'); +}); + +/* Symbol */ +add('es6-symbol', () => typeof global.Symbol !== 'undefined' && typeof Symbol() === 'symbol'); + /* WeakMap */ -add('es6-weakmap', function () { +add('es6-weakmap', () => { if (typeof global.WeakMap !== 'undefined') { /* IE11 and older versions of Safari are missing critical ES6 Map functionality */ const key1 = {}; @@ -129,13 +172,10 @@ add('es6-weakmap', function () { }); /* Miscellaneous features */ - -add('float32array', 'Float32Array' in global); -add('setimmediate', typeof global.setImmediate !== 'undefined'); -add('postmessage', typeof postMessage === 'function'); add('microtasks', () => has('es6-promise') || has('host-node') || has('dom-mutationobserver')); - -add('raf', typeof requestAnimationFrame === 'function'); +add('postmessage', () => typeof global.postMessage === 'function'); +add('raf', () => typeof global.requestAnimationFrame === 'function'); +add('setimmediate', () => typeof global.setImmediate !== 'undefined'); /* DOM Features */ diff --git a/src/support/util.ts b/src/support/util.ts index 7b1a5d4..c86324f 100644 --- a/src/support/util.ts +++ b/src/support/util.ts @@ -22,8 +22,8 @@ export function getValueDescriptor(value: T, enumerable: boolean = false, wri * * @param nativeFunction The source function to be wrapped */ -export function wrapNative(nativeFunction: Function): any { - return function (target: any, ...args: any[]) { +export function wrapNative(nativeFunction: (...args: U[]) => R): (target: T, ...args: U[]) => R { + return function (target: T, ...args: U[]): R { return nativeFunction.apply(target, args); }; } diff --git a/tests/support/util.ts b/tests/support/util.ts index 947223b..fab4799 100644 --- a/tests/support/util.ts +++ b/tests/support/util.ts @@ -1,8 +1,5 @@ -import { Thenable } from '../../src/interfaces'; -export { Thenable } from '../../src/interfaces'; - -export function isEventuallyRejected(promise: Thenable): Thenable { - return promise.then(function () { +export function isEventuallyRejected(promise: PromiseLike): PromiseLike { + return promise.then(function () { throw new Error('unexpected code path'); }, function () { return true; // expect rejection diff --git a/tests/unit/WeakMap.ts b/tests/unit/WeakMap.ts index 69a4a06..4600637 100644 --- a/tests/unit/WeakMap.ts +++ b/tests/unit/WeakMap.ts @@ -50,7 +50,7 @@ registerSuite({ const frozen: Key = {}; Object.freeze(frozen); - const map = new WeakMap([ + const map = new WeakMap([ [ key, 1 ], [ frozen, 2 ] ]); @@ -61,8 +61,8 @@ registerSuite({ assert.isTrue(map.delete(frozen), 'deleting frozen should return true'); assert.isFalse(map.has(frozen), 'frozen should not be in map'); assert.isFalse(map.delete(frozen), 'deleting frozen again should return false'); - assert.isFalse(map.delete(undefined), 'deleting undefined key should return false'); - assert.isFalse(map.delete(null), 'deleting null key should return false'); + assert.isFalse(map.delete(undefined as any), 'deleting undefined key should return false'); + assert.isFalse(map.delete(null as any), 'deleting null key should return false'); }, '.get'() { @@ -71,7 +71,7 @@ registerSuite({ const frozen: Key = {}; Object.freeze(frozen); - const map = new WeakMap([ + const map = new WeakMap([ [ key1, 1 ], [ key2, 2 ], [ frozen, 3] @@ -90,8 +90,8 @@ registerSuite({ assert.strictEqual(map.get(key1), undefined, 'key1 should still be undefined'); assert.strictEqual(map.get(key2), undefined, 'key2 should be undefined'); - assert.strictEqual(map.get(undefined), undefined, 'getting undefined value should just return undefined'); - assert.strictEqual(map.get(null), undefined, 'getting null value should just return undefined'); + assert.strictEqual(map.get(undefined as any), undefined, 'getting undefined value should just return undefined'); + assert.strictEqual(map.get(null as any), undefined, 'getting null value should just return undefined'); assert.strictEqual(map.get(frozen), 3, 'frozen should be 3'); }, @@ -103,7 +103,7 @@ registerSuite({ const frozen: Key = {}; Object.freeze(frozen); - const map = new WeakMap([ + const map = new WeakMap([ [ key1, 1 ], [ key3, 3 ], [ frozen, 5] @@ -113,8 +113,8 @@ registerSuite({ assert.isFalse(map.has(key2), 'key2 should not be in map'); assert.isTrue(map.has(key3), 'key3 should be in map'); - assert.isFalse(map.has(undefined), 'undefined key should not be in map'); - assert.isFalse(map.has(null), 'null key should not be in map'); + assert.isFalse(map.has(undefined as any), 'undefined key should not be in map'); + assert.isFalse(map.has(null as any), 'null key should not be in map'); assert.isTrue(map.has(frozen), 'frozen should be in the map'); }, diff --git a/tests/unit/all.ts b/tests/unit/all.ts index 4b20b6d..9a1ce16 100644 --- a/tests/unit/all.ts +++ b/tests/unit/all.ts @@ -1,4 +1,3 @@ -import './native/all'; import './support/decorators'; import './support/has'; import './support/queue'; diff --git a/tests/unit/array.ts b/tests/unit/array.ts index 87b4d5a..0e5f576 100644 --- a/tests/unit/array.ts +++ b/tests/unit/array.ts @@ -79,7 +79,7 @@ function createNativeAndDojoArrayTests(feature: string, tests: {}) { registerSuite({ name: 'array', - '.from()': createNativeAndDojoArrayTests('es6-array-from', { + '.from()': createNativeAndDojoArrayTests('es6-array', { 'from undefined: throws': function () { assert.throws(function () { array.from( undefined); @@ -229,7 +229,7 @@ registerSuite({ } }), - '.of()': createNativeAndDojoArrayTests('es6-array-of', { + '.of()': createNativeAndDojoArrayTests('es6-array', { 'empty arguments': function () { assert.deepEqual(array.of(), []); }, @@ -312,7 +312,7 @@ registerSuite({ } }), - '#findIndex()': createNativeAndDojoArrayTests('es6-array-findindex', (function () { + '#findIndex()': createNativeAndDojoArrayTests('es6-array', (function () { function callback(element: string) { return element === 'goose'; } @@ -357,7 +357,7 @@ registerSuite({ }; })()), - '#find()': createNativeAndDojoArrayTests('es6-array-find', (function () { + '#find()': createNativeAndDojoArrayTests('es6-array', (function () { function callback(element: number) { return element > 5; } @@ -375,7 +375,7 @@ registerSuite({ }; })()), - '#copyWithin()': createNativeAndDojoArrayTests('es6-array-copywithin', { + '#copyWithin()': createNativeAndDojoArrayTests('es6-array', { 'returns source array': function () { let arr: any[] = []; assert.equal(array.copyWithin(arr, 0, 0), arr); @@ -461,7 +461,7 @@ registerSuite({ })() }), - '#includes': createNativeAndDojoArrayTests('es7-array-includes', (function() { + '#includes': createNativeAndDojoArrayTests('es7-array', (function() { let arr: number[]; return { beforeEach() { @@ -491,7 +491,7 @@ registerSuite({ }; })()), - 'extension support': createDojoTests('es6-array-from', { + 'extension support': createDojoTests('es6-array', { '.from()': function () { let actual = MyArray.from([ 1, 2, 3 ]); assert.instanceOf(actual, MyArray); diff --git a/tests/unit/native/Map.ts b/tests/unit/native/Map.ts deleted file mode 100644 index 91832df..0000000 --- a/tests/unit/native/Map.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; -import global from '../../../src/global'; - -registerSuite({ - name: 'native/Map', - 'verify API'(this: any) { - if (!has('es6-map')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/Map' ], dfd.callback((m: any) => { - /* tslint:disable-next-line:variable-name */ - const Map = m.default; - const map = new Map(); - assert.instanceOf(map, global.Map); - })); - } -}); diff --git a/tests/unit/native/Promise.ts b/tests/unit/native/Promise.ts deleted file mode 100644 index 4ce2d33..0000000 --- a/tests/unit/native/Promise.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; -import global from '../../../src/global'; - -registerSuite({ - name: 'native/Promise', - 'verify API'(this: any) { - if (!has('es6-promise')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/Promise' ], dfd.callback((m: any) => { - /* tslint:disable-next-line:variable-name */ - const Promise = m.default; - const promise = new Promise((resolve: () => void) => { - resolve(); - }); - assert.instanceOf(promise, global.Promise); - })); - } -}); diff --git a/tests/unit/native/Set.ts b/tests/unit/native/Set.ts deleted file mode 100644 index da45de4..0000000 --- a/tests/unit/native/Set.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; -import global from '../../../src/global'; - -registerSuite({ - name: 'native/Set', - 'verify API'(this: any) { - if (!has('es6-set')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/Set' ], dfd.callback((m: any) => { - /* tslint:disable-next-line:variable-name */ - const Set = m.default; - const set = new Set(); - assert.instanceOf(set, global.Set); - })); - } -}); diff --git a/tests/unit/native/Symbol.ts b/tests/unit/native/Symbol.ts deleted file mode 100644 index 8f4d07c..0000000 --- a/tests/unit/native/Symbol.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; -import global from '../../../src/global'; - -registerSuite({ - name: 'native/Symbol', - 'verify API'(this: any) { - if (!has('es6-symbol')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/Symbol' ], dfd.callback((m: any) => { - /* tslint:disable-next-line:variable-name */ - const Symbol = m.default; - const { isSymbol } = m; - const sym = Symbol('foo'); - assert.typeOf(sym, 'symbol'); - assert.isTrue(isSymbol(sym)); - assert.isFalse(isSymbol('foo')); - assert.strictEqual(Symbol, global.Symbol); - [ - 'hasInstance', - 'isConcatSpreadable', - 'iterator', - 'species', - 'replace', - 'search', - 'split', - 'match', - 'toPrimitive', - 'toStringTag', - 'unscopables' - ].forEach((wellKnown) => assert.typeOf(Symbol[wellKnown], 'symbol')); - })); - } -}); diff --git a/tests/unit/native/WeakMap.ts b/tests/unit/native/WeakMap.ts deleted file mode 100644 index e6f9b0c..0000000 --- a/tests/unit/native/WeakMap.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; -import global from '../../../src/global'; - -registerSuite({ - name: 'native/WeakMap', - 'verify API'(this: any) { - if (!has('es6-weakmap')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/WeakMap' ], dfd.callback((m: any) => { - /* tslint:disable-next-line:variable-name */ - const WeakMap = m.default; - const weakmap = new WeakMap(); - assert.instanceOf(weakmap, global.WeakMap); - })); - } -}); diff --git a/tests/unit/native/all.ts b/tests/unit/native/all.ts deleted file mode 100644 index 633b52f..0000000 --- a/tests/unit/native/all.ts +++ /dev/null @@ -1,12 +0,0 @@ -import './array'; -import './iterator'; -import './main'; -import './Map'; -import './math'; -import './number'; -import './object'; -import './Promise'; -import './Set'; -import './string'; -import './Symbol'; -import './WeakMap'; diff --git a/tests/unit/native/array.ts b/tests/unit/native/array.ts deleted file mode 100644 index 94ac970..0000000 --- a/tests/unit/native/array.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; - -registerSuite({ - name: 'native/array', - 'verify API'(this: any) { - if (!has('es6-array-from')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/array' ], dfd.callback((array: any) => { - assert.isFunction(array.from); - assert.isFunction(array.of); - assert.isFunction(array.copyWithin); - assert.isFunction(array.fill); - assert.isFunction(array.find); - assert.isFunction(array.findIndex); - assert.isFunction(array.includes); - assert.strictEqual(Object.keys(array).length, 7); - })); - } -}); diff --git a/tests/unit/native/iterator.ts b/tests/unit/native/iterator.ts deleted file mode 100644 index 369c861..0000000 --- a/tests/unit/native/iterator.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has, { add as hasAdd } from '../../../src/support/has'; -import global from '../../../src/global'; - -hasAdd('es6-iterator', () => Boolean(global.Symbol && global.Symbol.iterator && global.Array.prototype[Symbol.iterator])); - -registerSuite({ - name: 'native/iterator', - 'verify API'(this: any) { - if (!has('es6-iterator')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/iterator' ], dfd.callback(function (iterator: any) { - assert.isFunction(iterator.isIterable); - assert.isFunction(iterator.isArrayLike); - assert.isFunction(iterator.get); - assert.isFunction(iterator.forOf); - assert.strictEqual(Object.keys(iterator).length, 4); - assert.isFunction(iterator.get([ 1, 2, 3 ]).next); - assert.isFunction(iterator.get('foo').next); - assert.isUndefined(iterator.get(1)); - - const results: any[] = []; - iterator.forOf([ 1, 2, 3, 4 ], (item: number, source: number[], doBreak: () => void) => { - results.push(item); - if (results.length === 3) { - doBreak(); - } - }); - assert.deepEqual(results, [ 1, 2, 3 ]); - assert.throws(() => { - iterator.forOf(true); - }, TypeError); - - assert.isTrue(iterator.isArrayLike(arguments)); - assert.isFalse(iterator.isArrayLike(false)); - })); - } -}); diff --git a/tests/unit/native/main.ts b/tests/unit/native/main.ts deleted file mode 100644 index 2b5e254..0000000 --- a/tests/unit/native/main.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; - -registerSuite({ - name: 'native/main', - 'verify API'(this: any) { - if (!has('es6-array-from')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/main' ], dfd.callback((main: any) => { - assert.isObject(main.array); - assert.isObject(main.iterator); - assert.isFunction(main.Map); - assert.isObject(main.math); - assert.isObject(main.number); - assert.isObject(main.object); - assert.isFunction(main.Promise); - assert.isFunction(main.Set); - assert.isObject(main.string); - assert.isFunction(main.Symbol); - assert.isFunction(main.WeakMap); - assert.strictEqual(Object.keys(main).length, 11); - })); - } -}); diff --git a/tests/unit/native/math.ts b/tests/unit/native/math.ts deleted file mode 100644 index 44f5ba0..0000000 --- a/tests/unit/native/math.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; - -registerSuite({ - name: 'native/math', - 'verify API'(this: any) { - if (!has('es6-math-acosh') || !has('es6-math-clz32')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/math' ], dfd.callback((math: any) => { - [ - 'acosh', - 'asinh', - 'atanh', - 'cbrt', - 'clz32', - 'cosh', - 'expm1', - 'fround', - 'hypot', - 'imul', - 'log2', - 'log10', - 'log1p', - 'sign', - 'sinh', - 'tanh', - 'trunc' - ].forEach((method: string) => assert.isFunction(math[method], `Math "${method}" is not defined`)); - assert.strictEqual(Object.keys(math).length, 17); - })); - } -}); diff --git a/tests/unit/native/number.ts b/tests/unit/native/number.ts deleted file mode 100644 index 5955826..0000000 --- a/tests/unit/native/number.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has, { add as hasAdd } from '../../../src/support/has'; -import global from '../../../src/global'; - -hasAdd('es6-number', 'EPSILON' in global.Number); - -registerSuite({ - name: 'native/number', - 'verify API'(this: any) { - if (!has('es6-number')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/number' ], dfd.callback((num: any) => { - [ - 'EPSILON', - 'MAX_SAFE_INTEGER', - 'MIN_SAFE_INTEGER' - ].forEach((value) => assert.isNumber(num[value])); - [ - 'isNaN', - 'isFinite', - 'isInteger', - 'isSafeInteger' - ].forEach((method) => assert.isFunction(num[method], `'${method}' should be a function`)); - assert.strictEqual(Object.keys(num).length, 7); - })); - } -}); diff --git a/tests/unit/native/object.ts b/tests/unit/native/object.ts deleted file mode 100644 index 90fbbeb..0000000 --- a/tests/unit/native/object.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has, { add as hasAdd } from '../../../src/support/has'; -import global from '../../../src/global'; - -hasAdd('es6-object', 'getOwnPropertySymbols' in global.Object); - -registerSuite({ - name: 'native/object', - 'verify API'(this: any) { - if (!has('es6-object')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/object' ], dfd.callback((object: any) => { - [ - 'is', - 'getOwnPropertySymbols', - 'getOwnPropertyNames', - 'keys' - ].forEach((method) => assert.isFunction(object[method])); - assert.strictEqual(Object.keys(object).length, 4); - })); - } -}); diff --git a/tests/unit/native/string.ts b/tests/unit/native/string.ts deleted file mode 100644 index c33dc6e..0000000 --- a/tests/unit/native/string.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as registerSuite from 'intern!object'; -import * as assert from 'intern/chai!assert'; -import has from '../../../src/support/has'; - -registerSuite({ - name: 'native/string', - 'verify API'(this: any) { - if (!has('es6-string-raw')) { - this.skip('No native support'); - } - const dfd = this.async(); - ( require)([ 'src/native/string' ], dfd.callback((str: any) => { - [ - 'HIGH_SURROGATE_MIN', - 'HIGH_SURROGATE_MAX', - 'LOW_SURROGATE_MIN', - 'LOW_SURROGATE_MAX' - ].forEach((prop: string) => assert.isNumber(str[prop])); - - [ - 'raw', - 'fromCodePoint', - 'codePointAt', - 'repeat', - 'startsWith', - 'endsWith', - 'includes' - ].forEach((method: string) => assert.isFunction(str[method])); - - assert.strictEqual(Object.keys(str).length, 11); - })); - } -}); diff --git a/tests/unit/object.ts b/tests/unit/object.ts index 8654097..32fb4b7 100644 --- a/tests/unit/object.ts +++ b/tests/unit/object.ts @@ -1,11 +1,134 @@ import * as registerSuite from 'intern!object'; import * as assert from 'intern/chai!assert'; import * as object from '../../src/object'; +import { assign } from '../../src/object'; import 'src/Symbol'; registerSuite({ name: 'object', + '.assign()': { + '.assign()'() { + const source: { + a: number + b: { + enumerable: boolean, + configurable: boolean, + writable: boolean, + value: number + } + } = Object.create({ a: 1 }, { + b: { + enumerable: false, + configurable: true, + writable: true, + value: 2 + } + }); + ( source).c = 3; + ( source).nested = { a: 5 }; + + const object: { + c: number, + nested: { + a: number + } + } = Object.create(null); + const assignedObject = assign(object, source); + + assert.strictEqual(object, assignedObject, 'assign should return the modified target object'); + assert.isUndefined(assignedObject.a, 'assign should not copy inherited properties'); + assert.isUndefined(assignedObject.b, 'assign should not copy non-enumerable properties'); + assert.strictEqual(assignedObject.c, 3); + assert.strictEqual(assignedObject.nested, ( source).nested, 'assign should perform a shallow copy'); + assert.strictEqual(assignedObject.nested.a, 5); + }, + + '.assign() with multiple sources'() { + let source1 = { + property3: 'value3', + property4: 'value4' + }; + + let source3 = { + property7: 'value7', + property8: 'value8' + }; + + const object = { + property1: 'value1', + property2: 'value2' + }; + + assign(object, source1, null, source3); + + assert.deepEqual(object, { + property1: 'value1', + property2: 'value2', + property3: 'value3', + property4: 'value4', + property7: 'value7', + property8: 'value8' + }); + }, + + '.assign() with inferred type from multiple sources'() { + let source1: { a: number, b: number } | { c: number, d: number } = { + a: 1, + b: 2, + c: 3, + d: 4 + }; + + let source2 = { + a: 3, + b: 2 + }; + + let source3 = { + c: 3, + d: 4 + }; + + const object = {}; + + const assignedObject = assign(object, source1, source2, source3); + + assert(assignedObject); + + // Verify that the inferred type is what we expect + const alsoAssigned: {} & ({ a: number, b: number } | { c: number, d: number }) = assign(object, source1, source2, source3); + + assert(alsoAssigned); + }, + + '.assign() with different types of sources'() { + const baseObject = { + foo: 'foo' + }; + + const assignedObject = assign({}, baseObject, { bar: 'bar' }); + assert.strictEqual(assignedObject.bar, 'bar'); + assert.strictEqual(assignedObject.foo, 'foo'); + }, + '.assign() with a source whose type is a subset of another'() { + type FooBar = { + foo: string; + bar: string; + }; + const foobar = { + foo: 'foo', + bar: 'bar' + }; + const bar = { + bar: 'baz' + }; + const assignedObject = assign({}, foobar, bar); + assert.strictEqual(assignedObject.foo, 'foo'); + assert.strictEqual(assignedObject.bar, 'baz'); + } + }, + '.is()': { 'two identical strings'() { assert.isTrue(object.is('foo', 'foo')); diff --git a/tests/unit/string.ts b/tests/unit/string.ts index ff5eac2..1378b85 100644 --- a/tests/unit/string.ts +++ b/tests/unit/string.ts @@ -1,7 +1,6 @@ import * as registerSuite from 'intern!object'; import * as assert from 'intern/chai!assert'; import * as stringUtil from '../../src/string'; -import has from '../../src/support/has'; registerSuite({ name: 'string functions', @@ -68,15 +67,7 @@ registerSuite({ }, 'position is Infinity, not included, or NaN'() { - /* For some reason, native implementations of endsWith do not treat `null` - * and `NaN` like `Infinity` and `undefined`, so there are differences in - * the results if we are offloading to native or not. - * Tried to address the issues, but it is a difficult behaviour to replicate, - * and since the behaviour is really an edge case, going to just test for the - * two different behaviours anyways */ - let counts = has('es6-string-endswith') - ? [ Infinity, undefined ] - : [ Infinity, undefined, null, NaN ]; + const counts = [ Infinity, undefined as any, null, NaN ]; for (let count of counts) { assert.isTrue(stringUtil.endsWith('abc', '', count)); assert.isFalse(stringUtil.endsWith('abc', '\0', count)); @@ -86,18 +77,6 @@ registerSuite({ assert.isTrue(stringUtil.endsWith('abc', 'abc', count)); assert.isFalse(stringUtil.endsWith('abc', 'abcd', count)); } - if (has('es6-string-endswith')) { - counts = [ null, NaN ]; - for (let count of counts) { - assert.isTrue(stringUtil.endsWith('abc', '', count)); - assert.isFalse(stringUtil.endsWith('abc', '\0', count)); - assert.isFalse(stringUtil.endsWith('abc', 'c', count)); - assert.isFalse(stringUtil.endsWith('abc', 'b', count)); - assert.isFalse(stringUtil.endsWith('abc', 'bc', count)); - assert.isFalse(stringUtil.endsWith('abc', 'abc', count)); - assert.isFalse(stringUtil.endsWith('abc', 'abcd', count)); - } - } }, 'position is 0 or negative'() { diff --git a/tests/unit/support/has.ts b/tests/unit/support/has.ts index 3867536..93e0a60 100644 --- a/tests/unit/support/has.ts +++ b/tests/unit/support/has.ts @@ -7,33 +7,27 @@ registerSuite({ 'features defined'() { [ - 'es6-object-assign', - 'es6-array-from', - 'es6-array-of', + 'dom-mutationobserver', + 'es-observable', + 'es2017-object', + 'es2017-string', + 'es6-array', 'es6-array-fill', - 'es6-array-findindex', - 'es6-array-find', - 'es6-array-copywithin', - 'es7-array-includes', - 'es6-string-raw', - 'es6-string-fromcodepoint', - 'es6-string-codepointat', - 'es6-string-normalize', - 'es6-string-repeat', - 'es6-string-startswith', - 'es6-string-endswith', - 'es6-string-includes', + 'es6-map', + 'es6-math', 'es6-math-imul', + 'es6-object', 'es6-promise', 'es6-set', - 'es6-map', - 'es6-weakmap', + 'es6-string', + 'es6-string-raw', 'es6-symbol', - 'float32array', - 'setimmediate', - 'postmessage', + 'es6-weakmap', + 'es7-array', 'microtasks', - 'dom-mutationobserver' + 'postmessage', + 'raf', + 'setimmediate' ].forEach((feature) => assert.isTrue(exists(feature))); } }); diff --git a/tsconfig.json b/tsconfig.json index 26f3572..36d0fe2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "dom", "es5", "es2015.iterable", + "es2015.promise", "es2015.symbol", "es2015.symbol.wellknown" ], diff --git a/tslint.json b/tslint.json index 855e134..738b8e0 100644 --- a/tslint.json +++ b/tslint.json @@ -59,7 +59,7 @@ "variable-declaration": "onespace" } ], "use-strict": false, - "variable-name": [ true, "check-format", "allow-leading-underscore", "ban-keywords" ], + "variable-name": [ true, "check-format", "allow-leading-underscore", "allow-pascal-case", "ban-keywords" ], "whitespace": [ true, "check-branch", "check-decl", "check-operator", "check-module", "check-separator", "check-type", "check-typecast" ] } }