Update Ember, RSVP, and Ember testing helpers. (#20301)

* Update RSVP to 4.0, to implement PromiseLike<T>.

- RSVP Promises can now be used with `async` and `await`.
- RSVP types now match what is in RSVP 4.0.

* Update ember-testing-helpers for fixed RSVP.

* Ember.js: correctly represent most of the framework.

- Capture the actual behavior of most of the framework, including computed
  properties, custom getters and setters and the custom Object model more
  generally, prototype extension via `.extend`, and the mixin pattern.
- Support the new modules API alongside the global API.
- Add extensive tests.
- Update inline documentation.
- Use the new, async/await compatible RSVP definitions.

* Ember/RSVP: drop .prettierrc files.

* Drop types/rsvp/assert.ts -- stick to just rsvp-test.ts.

* Fix ember-testing-helpers-tests on top of module itself.

* Fix RSVP import in ember-testing-helpers.

* Fix 'typeRoots', set ember-testing-helpers to use TS 2.4.

* Fix missing 'types' compiler option.

* Fix errors caught by dtslint.

* A few more tslint tweaks.

* Fix account link in ember-testing-helpers authorship.

* Disable strictFunctionTypes for Ember, RSVP.

* fix array.reduce signature conflict in ts@next
This commit is contained in:
Chris Krycho 2017-10-13 06:13:17 -04:00 committed by Wesley Wigham
parent e176bdbdff
commit dfa4555106
36 changed files with 5943 additions and 3047 deletions

View File

@ -1,12 +1,12 @@
import RSVP = require('rsvp');
import RSVP from 'rsvp';
function testAndThen() {
const result: RSVP.Promise<string, never> = andThen(() => 'some string');
const result: RSVP.Promise<string> = andThen(() => 'some string');
result.then(s => s.length);
}
function testClick() {
const result: RSVP.Promise<void, never> = click('someString');
const result: RSVP.Promise<void> = click('someString');
result.then(() => {});
}
@ -29,7 +29,7 @@ function testFillIn() {
const textResult = fillIn('.foo', 'waffles');
textResult.then(() => true);
const contextResult = fillIn('.bar', {}, 'pancakes');
contextResult.catch(reason => false);
contextResult.catch((reason: any) => false);
}
function testFind() {

View File

@ -1,8 +1,8 @@
// Type definitions for ember-testing/lib/helpers
// Project: https://github.com/emberjs/ember.js/tree/master/packages/ember-testing/lib/helpers
// Definitions by: Chris Krycho <github.com/chriskrycho>
// Definitions by: Chris Krycho <https://github.com/chriskrycho>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 2.4
// Note that these are distributed separately because they represent a discrete
// set of functionality, and as globally-injected items (as of Ember 2.13), are
@ -10,14 +10,14 @@
/// <reference types="jquery" />
import RSVP = require('rsvp');
import RSVP from 'rsvp';
type KeyEventType = 'keydown' | 'keyup' | 'keypress';
type WaitResult<T> = RSVP.Promise<T, never>;
type WaitResult<T> = RSVP.Promise<T>;
declare global {
// https://github.com/emberjs/ember.js/blob/master/packages/ember-testing/lib/helpers/and_then.js
function andThen<T>(callback: (...args: any[]) => T): RSVP.Promise<T, never>;
function andThen<T>(callback: (...args: any[]) => T): RSVP.Promise<T>;
// https://github.com/emberjs/ember.js/blob/master/packages/ember-testing/lib/helpers/click.js
function click(selector: string, context?: Object): WaitResult<void>;
@ -45,7 +45,7 @@ declare global {
function keyEvent(selector: string, type: KeyEventType, keyCode: number): WaitResult<void>;
// https://github.com/emberjs/ember.js/blob/master/packages/ember-testing/lib/helpers/pause_test.js
function pauseTest(): RSVP.Promise<{}, never>;
function pauseTest(): RSVP.Promise<{}>;
function resumeTest(): void;
// https://github.com/emberjs/ember.js/blob/master/packages/ember-testing/lib/helpers/trigger_event.js

View File

@ -10,9 +10,7 @@
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"typeRoots": ["../"],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true

6151
types/ember/index.d.ts vendored Normal file → Executable file

File diff suppressed because it is too large Load Diff

15
types/ember/test/application.ts Executable file
View File

@ -0,0 +1,15 @@
import Ember from 'ember';
import { assertType } from "./lib/assert";
let App = Ember.Application.create({
customEvents: {
paste: 'paste'
}
});
let App2 = Ember.Application.create({
customEvents: {
mouseenter: null,
mouseleave: null
}
});

18
types/ember/test/array-ext.ts Executable file
View File

@ -0,0 +1,18 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
declare global {
interface Array<T> extends Ember.ArrayPrototypeExtensions<T> {}
}
class Person extends Ember.Object {
name: string;
}
const person = Person.create({ name: 'Joe' });
const array = [person];
assertType<number>(array.get('length'));
assertType<Person | undefined>(array.get('firstObject'));
assertType<string[]>(array.mapBy('name'));
assertType<string[]>(array.map(p => p.get('name')));

26
types/ember/test/array-proxy.ts Executable file
View File

@ -0,0 +1,26 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const pets = ['dog', 'cat', 'fish'];
const proxy = Ember.ArrayProxy.create({ content: Ember.A(pets) });
proxy.get('firstObject'); // 'dog'
proxy.set('content', Ember.A(['amoeba', 'paramecium']));
proxy.get('firstObject'); // 'amoeba'
const overridden = Ember.ArrayProxy.create({
content: Ember.A(pets),
objectAtContent(idx: number): string {
return this.get('content').objectAt(idx)!.toUpperCase();
}
});
overridden.get('firstObject'); // 'DOG'
class MyNewProxy<T> extends Ember.ArrayProxy<T> {
isNew = true;
}
let x: MyNewProxy<number> = MyNewProxy.create({ content: Ember.A([1, 2, 3]) });
assertType<number | undefined>(x.get('firstObject'));
assertType<boolean>(x.isNew);

46
types/ember/test/array.ts Executable file
View File

@ -0,0 +1,46 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
type Person = typeof Person.prototype;
const Person = Ember.Object.extend({
name: '',
isHappy: false
});
const people = Ember.A([
Person.create({ name: 'Yehuda', isHappy: true }),
Person.create({ name: 'Majd', isHappy: false }),
]);
assertType<number>(people.get('length'));
assertType<Person>(people.get('lastObject'));
assertType<boolean>(people.isAny('isHappy'));
assertType<boolean>(people.isAny('isHappy', false));
assertType<Person[]>(people.filterBy('isHappy'));
assertType<typeof people>(people.get('[]'));
assertType<Person>(people.get('[]').get('firstObject'));
assertType<Ember.Array<boolean>>(people.mapBy('isHappy'));
assertType<any[]>(people.mapBy('name.length'));
const last = people.get('lastObject');
if (last) {
assertType<string>(last.get('name'));
}
const first = people.get('lastObject');
if (first) {
assertType<boolean>(first.get('isHappy'));
}
const letters: Ember.Enumerable<string> = Ember.A(['a', 'b', 'c']);
const codes: number[] = letters.map((item, index, enumerable) => {
assertType<string>(item);
assertType<number>(index);
return item.charCodeAt(0);
});
let value = '1,2,3';
let filters = Ember.A(value.split(','));
filters.push('4');
filters.sort();

124
types/ember/test/component.ts Executable file
View File

@ -0,0 +1,124 @@
import Ember from 'ember';
import Component from '@ember/component';
import Object, { computed } from '@ember/object';
import hbs from 'htmlbars-inline-precompile';
import { assertType } from "./lib/assert";
Component.extend({
layout: hbs`
<div>
{{yield}}
</div>
`,
});
Component.extend({
layout: 'my-layout',
});
const MyComponent = Component.extend();
assertType<string | string[]>(Ember.get(MyComponent, 'positionalParams'));
const component1 = Component.extend({
actions: {
hello(name: string) {
console.log('Hello', name);
},
},
});
Component.extend({
name: '',
hello(name: string) {
this.set('name', name);
},
});
Component.extend({
tagName: 'em',
});
Component.extend({
classNames: ['my-class', 'my-other-class'],
});
Component.extend({
classNameBindings: ['propertyA', 'propertyB'],
propertyA: 'from-a',
propertyB: computed(function() {
if (!this.get('propertyA')) {
return 'from-b';
}
}),
});
Component.extend({
classNameBindings: ['hovered'],
hovered: true,
});
Component.extend({
classNameBindings: ['messages.empty'],
messages: Object.create({
empty: true,
}),
});
Component.extend({
classNameBindings: ['isEnabled:enabled:disabled'],
isEnabled: true,
});
Component.extend({
classNameBindings: ['isEnabled::disabled'],
isEnabled: true,
});
Component.extend({
tagName: 'a',
attributeBindings: ['href'],
href: 'http://google.com',
});
Component.extend({
tagName: 'a',
attributeBindings: ['url:href'],
url: 'http://google.com',
});
Component.extend({
tagName: 'use',
attributeBindings: ['xlinkHref:xlink:href'],
xlinkHref: '#triangle',
});
Component.extend({
tagName: 'input',
attributeBindings: ['disabled'],
disabled: false,
});
Component.extend({
tagName: 'input',
attributeBindings: ['disabled'],
disabled: computed(() => {
if ('someLogic') {
return true;
} else {
return false;
}
}),
});
Component.extend({
tagName: 'form',
attributeBindings: ['novalidate'],
novalidate: null,
});
Component.extend({
click(event: object) {
// will be called when an instance's
// rendered element is clicked
},
});

160
types/ember/test/computed.ts Executable file
View File

@ -0,0 +1,160 @@
import Ember from 'ember';
import Component from '@ember/component';
import { or } from '@ember/object/computed';
import { assertType } from './lib/assert';
const Person = Ember.Object.extend({
firstName: '',
lastName: '',
age: 0,
noArgs: Ember.computed<string>(() => 'test'),
fullName: Ember.computed<string>('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
}),
fullNameReadonly: Ember.computed<string>('fullName', function() {
return this.get('fullName');
}).readOnly(),
fullNameWritable: Ember.computed<string>('firstName', 'lastName', {
get() {
return this.get('fullName');
},
set(key, value) {
let [first, last] = value.split(' ');
this.set('firstName', first);
this.set('lastName', last);
return value;
}
}),
fullNameGetOnly: Ember.computed<string>('fullName', {
get() {
return this.get('fullName');
}
}),
fullNameSetOnly: Ember.computed<string>('firstName', 'lastName', {
set(key, value) {
let [first, last] = value.split(' ');
this.set('firstName', first);
this.set('lastName', last);
return value;
}
}),
combinators: Ember.computed<string>(function() {
return this.get('firstName');
}).property('firstName')
.meta({ foo: 'bar' })
.volatile()
.readOnly()
});
const person = Person.create({
firstName: 'Fred',
lastName: 'Smith',
age: 29,
});
assertType<string>(person.firstName);
assertType<number>(person.age);
assertType<Ember.ComputedProperty<string>>(person.noArgs);
assertType<Ember.ComputedProperty<string>>(person.fullName);
assertType<Ember.ComputedProperty<string>>(person.fullNameReadonly);
assertType<Ember.ComputedProperty<string>>(person.fullNameWritable);
assertType<Ember.ComputedProperty<string>>(person.fullNameGetOnly);
assertType<Ember.ComputedProperty<string>>(person.fullNameSetOnly);
assertType<Ember.ComputedProperty<string>>(person.combinators);
assertType<string>(person.get('firstName'));
assertType<number>(person.get('age'));
assertType<string>(person.get('noArgs'));
assertType<string>(person.get('fullName'));
assertType<string>(person.get('fullNameReadonly'));
assertType<string>(person.get('fullNameWritable'));
assertType<string>(person.get('fullNameGetOnly'));
assertType<string>(person.get('fullNameSetOnly'));
assertType<string>(person.get('combinators'));
assertType<{ firstName: string, fullName: string, age: number }>(person.getProperties('firstName', 'fullName', 'age'));
const person2 = Person.create({
fullName: 'Fred Smith'
});
assertType<string>(person2.get('firstName'));
assertType<string>(person2.get('fullName'));
const person3 = Person.extend({
firstName: 'Fred',
fullName: 'Fred Smith'
}).create();
assertType<string>(person3.get('firstName'));
assertType<string>(person3.get('fullName'));
const person4 = Person.extend({
firstName: Ember.computed(() => 'Fred'),
fullName: Ember.computed(() => 'Fred Smith')
}).create();
assertType<string>(person4.get('firstName'));
assertType<string>(person4.get('fullName'));
// computed property macros
const objectWithComputedProperties = Ember.Object.extend({
alias: Ember.computed.alias('foo'),
and: Ember.computed.and('foo', 'bar', 'baz', 'qux'),
bool: Ember.computed.bool('foo'),
collect: Ember.computed.collect('foo', 'bar', 'baz', 'qux'),
deprecatingAlias: Ember.computed.deprecatingAlias('foo', {
id: 'hamster.deprecate-banana',
until: '3.0.0'
}),
empty: Ember.computed.empty('foo'),
equalNumber: Ember.computed.equal('foo', 1),
equalString: Ember.computed.equal('foo', 'bar'),
equalObject: Ember.computed.equal('foo', {}),
filter: Ember.computed.filter('foo', (item) => item === 'bar'),
filterBy1: Ember.computed.filterBy('foo', 'bar'),
filterBy2: Ember.computed.filterBy('foo', 'bar', false),
gt: Ember.computed.gt('foo', 3),
gte: Ember.computed.gte('foo', 3),
intersect: Ember.computed.intersect('foo', 'bar', 'baz', 'qux'),
lt: Ember.computed.lt('foo', 3),
lte: Ember.computed.lte('foo', 3),
map: Ember.computed.map('foo', (item, index) => item.bar),
mapBy: Ember.computed.mapBy('foo', 'bar'),
match: Ember.computed.match('foo', /^tom.ter$/),
max: Ember.computed.max('foo'),
min: Ember.computed.min('foo'),
none: Ember.computed.none('foo'),
not: Ember.computed.not('foo'),
notEmpty: Ember.computed.notEmpty('foo'),
oneWay: Ember.computed.oneWay('foo'),
or: Ember.computed.or('foo', 'bar', 'baz', 'qux'),
readOnly: Ember.computed.readOnly('foo'),
reads: Ember.computed.reads('foo'),
setDiff: Ember.computed.setDiff('foo', 'bar'),
sort1: Ember.computed.sort('foo', 'bar'),
sort2: Ember.computed.sort('foo', (itemA, itemB) => {
if (itemA < itemB) {
return -1;
} else if (itemA > itemB) {
return 1;
} else {
return 0;
}
}),
sum: Ember.computed.sum('foo'),
union: Ember.computed.union('foo', 'bar', 'baz', 'qux'),
uniq: Ember.computed.uniq('foo'),
uniqBy: Ember.computed.uniqBy('foo', 'bar')
});
const component2 = Component.extend({
isAnimal: or('isDog', 'isCat')
});

11
types/ember/test/controller.ts Executable file
View File

@ -0,0 +1,11 @@
import Controller from '@ember/controller';
Controller.extend ({
queryParams: ['category'],
category: null,
isExpanded: false,
toggleBody() {
this.toggleProperty('isExpanded');
}
});

7
types/ember/test/create.ts Executable file
View File

@ -0,0 +1,7 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const obj = Ember.Object.create({ a: 1 }, { b: 2 }, { c: 3 });
assertType<number>(obj.a);
assertType<number>(obj.b);
assertType<number>(obj.c);

View File

@ -0,0 +1,20 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const ExtendClass = Ember.Object.extend({
foo: 'hello'
});
class ES6Class extends Ember.Object {
bar: string;
}
let testObject = null;
if (ExtendClass.detectInstance(testObject)) {
assertType<string>(testObject.foo);
}
if (ES6Class.detectInstance(testObject)) {
assertType<string>(testObject.bar);
}

20
types/ember/test/detect.ts Executable file
View File

@ -0,0 +1,20 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const ExtendClass = Ember.Object.extend({
foo: 'hello'
});
class ES6Class extends Ember.Object {
bar: string;
}
let TestClass = Ember.Object;
if (ExtendClass.detect(TestClass)) {
assertType<string>(TestClass.create().foo);
}
if (ES6Class.detect(TestClass)) {
assertType<string>(TestClass.create().bar);
}

View File

@ -2,8 +2,7 @@ import Ember from 'ember';
let App: any;
App = Ember.Application.create<Ember.Application>();
App = Ember.Application.create();
App.president = Ember.Object.create({
name: 'Barack Obama',
});
@ -23,8 +22,9 @@ App.president.get('fullName');
declare class MyPerson extends Ember.Object {
static createMan(): MyPerson;
}
MyPerson.createMan();
const Person1 = Ember.Object.extend<typeof MyPerson>({
const Person1 = Ember.Object.extend({
say: (thing: string) => {
alert(thing);
},
@ -33,7 +33,9 @@ const Person1 = Ember.Object.extend<typeof MyPerson>({
declare class MyPerson2 extends Ember.Object {
helloWorld(): void;
}
const tom = Person1.create<MyPerson2>({
MyPerson2.create().helloWorld();
const tom = Person1.create({
name: 'Tom Dale',
helloWorld() {
this.say('Hi my name is ' + this.get('name'));
@ -41,23 +43,8 @@ const tom = Person1.create<MyPerson2>({
});
tom.helloWorld();
Person1.reopen({ isPerson: true });
Person1.create<Ember.Object>().get('isPerson');
Person1.reopenClass({
createMan: () => {
return Person1.create({ isMan: true });
},
});
// ReSharper disable once DuplicatingLocalDeclaration
Person1.createMan().get('isMan');
const person = Person1.create<Ember.Object>({
firstName: 'Yehuda',
lastName: 'Katz',
});
person.addObserver('fullName', null, () => {});
person.set('firstName', 'Brohuda');
const PersonReopened = Person1.reopen({ isPerson: true });
PersonReopened.create().get('isPerson');
App.todosController = Ember.Object.create({
todos: [Ember.Object.create({ isDone: false })],
@ -105,17 +92,6 @@ App.userController = Ember.Object.create({
}),
});
Ember.Helper.helper(params => {
const cents = params[0];
return `${cents * 0.01}`;
});
Ember.Helper.helper((params, hash) => {
const cents = params[0];
const currency = hash.currency;
return `${currency}${cents * 0.01}`;
});
Handlebars.registerHelper(
'highlight',
(property: string, options: any) =>
@ -124,7 +100,8 @@ Handlebars.registerHelper(
const coolView = App.CoolView.create();
const Person2 = Ember.Object.extend<typeof Ember.Object>({
const Person2 = Ember.Object.extend({
name: '',
sayHello() {
console.log('Hello from ' + this.get('name'));
},
@ -140,24 +117,25 @@ const arr = Ember.A([Ember.Object.create(), Ember.Object.create()]);
arr.setEach('name', 'unknown');
arr.getEach('name');
const Person3 = Ember.Object.extend<typeof Ember.Object>({
name: null,
const Person3 = Ember.Object.extend({
name: '',
isHappy: false,
});
const people2 = Ember.A([
Person3.create({ name: 'Yehuda', isHappy: true }),
Person3.create({ name: 'Majd', isHappy: false }),
]);
const isHappy = (person: Ember.Object): Boolean => {
const isHappy = (person: typeof Person3.prototype): boolean => {
return !!person.get('isHappy');
};
people2.every(isHappy);
people2.any(isHappy);
people2.everyProperty('isHappy', true);
people2.someProperty('isHappy', true);
people2.isEvery('isHappy', true);
people2.isAny('isHappy', true);
people2.isAny('isHappy');
// Examples taken from http://emberjs.com/api/classes/Em.RSVP.Promise.html
const promise = new Ember.RSVP.Promise<string, string>((resolve: Function, reject: Function) => {
const promise = new Ember.RSVP.Promise<string>((resolve: Function, reject: Function) => {
// on success
resolve('ok!');
@ -174,6 +152,9 @@ promise.then(
}
);
// make sure Ember.RSVP.Promise can be reference as a type
declare function promiseReturningFunction(urn: string): Ember.RSVP.Promise<string>;
const mix1 = Ember.Mixin.create({
foo: 1,
});
@ -182,27 +163,7 @@ const mix2 = Ember.Mixin.create({
bar: 2,
});
const mix3 = Ember.Mixin.create({
foo: 3,
});
const mix4 = Ember.Mixin.create({
bar: 4,
});
const mix5 = Ember.Mixin.create({
foo: 5,
});
const mix6 = Ember.Mixin.create({
bar: 6,
});
const mix7 = Ember.Mixin.create({
foo: 7,
});
const component1 = Ember.Component.extend(mix1, mix2, mix3, mix4, mix5, mix6, mix7, {
const component1 = Ember.Component.extend(mix1, mix2, {
lyft: Ember.inject.service(),
cars: Ember.computed.readOnly('lyft.cars'),
});

58
types/ember/test/event.ts Executable file
View File

@ -0,0 +1,58 @@
import Ember from 'ember';
function testOn() {
let Job = Ember.Object.extend({
logCompleted: Ember.on('completed', function() {
console.log('Job completed!');
})
});
let job = Job.create();
Ember.sendEvent(job, 'completed'); // Logs 'Job completed!'
}
function testEvented() {
let Person = Ember.Object.extend(Ember.Evented, {
greet() {
this.trigger('greet');
}
});
let person = Person.create();
person.on('greet', function() {
console.log('Our person has greeted');
});
person.on('greet', function() {
console.log('Our person has greeted');
}).one('greet', function() {
console.log('Offer one-time special');
}).off('event', {}, function() {});
person.greet();
}
function testObserver() {
Ember.Object.extend({
valueObserver: Ember.observer('value', function() {
// Executes whenever the "value" property changes
})
});
}
function testListener() {
Ember.Component.extend({
init() {
Ember.addListener(this, 'willDestroyElement', this, 'willDestroyListener');
Ember.addListener(this, 'willDestroyElement', this, 'willDestroyListener', true);
Ember.addListener(this, 'willDestroyElement', this, this.willDestroyListener);
Ember.addListener(this, 'willDestroyElement', this, this.willDestroyListener, true);
Ember.removeListener(this, 'willDestroyElement', this, 'willDestroyListener');
Ember.removeListener(this, 'willDestroyElement', this, this.willDestroyListener);
},
willDestroyListener() {
}
});
}

61
types/ember/test/extend.ts Executable file
View File

@ -0,0 +1,61 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const Person = Ember.Object.extend({
firstName: '',
lastName: '',
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
getFullName2(): string {
return `${this.get('firstName')} ${this.get('lastName')}`;
}
});
assertType<string>(Person.prototype.firstName);
assertType<() => string>(Person.prototype.getFullName);
const person = Person.create({
firstName: 'Joe',
lastName: 'Blow',
extra: 42
});
assertType<string>(person.getFullName());
assertType<number>(person.extra);
class ES6Person extends Ember.Object {
firstName: string;
lastName: string;
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get fullName2(): string {
return `${this.get('firstName')} ${this.get('lastName')}`;
}
}
assertType<string>(ES6Person.prototype.firstName);
assertType<string>(ES6Person.prototype.fullName);
const es6Person = ES6Person.create({
firstName: 'Joe',
lastName: 'Blow',
extra: 42
});
assertType<string>(es6Person.fullName);
assertType<number>(es6Person.extra);
class PersonWithStatics extends Ember.Object {
static isPerson = true;
}
const PersonWithStatics2 = PersonWithStatics.extend({});
class PersonWithStatics3 extends PersonWithStatics {}
class PersonWithStatics4 extends PersonWithStatics2 {}
assertType<boolean>(PersonWithStatics.isPerson);
assertType<boolean>(PersonWithStatics2.isPerson);
assertType<boolean>(PersonWithStatics3.isPerson);
assertType<boolean>(PersonWithStatics4.isPerson);

View File

@ -0,0 +1,21 @@
import Ember from 'ember';
declare global {
interface Function extends Ember.FunctionPrototypeExtensions {}
}
Ember.Object.extend({
foo: '',
arr: function() {
return [];
}.property(),
alias: function(this: any) {
return this.get('foo');
}.property('foo', 'bar.@each.baz'),
observer: function() {}.observes('foo', 'bar'),
on: function() {}.on('foo', 'bar'),
});

27
types/ember/test/helper.ts Executable file
View File

@ -0,0 +1,27 @@
import Ember from 'ember';
const FormatCurrencyHelper = Ember.Helper.helper(function(params, hash: { currency: string }) {
let cents = params[0];
let currency = hash.currency;
return `${currency}${cents * 0.01}`;
});
class User extends Ember.Object {
email: string;
}
class SessionService extends Ember.Service {
currentUser: User;
}
const CurrentUserEmailHelper = Ember.Helper.extend({
session: Ember.inject.service() as Ember.ComputedProperty<SessionService>,
onNewUser: Ember.observer('session.currentUser', function(this: Ember.Helper) {
this.recompute();
}),
compute(): string {
return this.get('session')
.get('currentUser')
.get('email');
},
});

20
types/ember/test/inject.ts Executable file
View File

@ -0,0 +1,20 @@
import Ember from 'ember';
class AuthService extends Ember.Service {
isAuthenticated: boolean;
}
class ApplicationController extends Ember.Controller {
transitionToLogin() {}
}
class LoginRoute extends Ember.Route {
auth = Ember.inject.service('authentication') as Ember.ComputedProperty<AuthService>;
application = Ember.inject.controller() as Ember.ComputedProperty<ApplicationController>;
didTransition() {
if (!this.get('auth').get('isAuthenticated')) {
this.get('application').transitionToLogin();
}
}
}

5
types/ember/test/lib/assert.ts Executable file
View File

@ -0,0 +1,5 @@
/** Static assertion that `value` has type `T` */
// Disable tslint here b/c the generic is used to let us do a type coercion and
// validate that coercion works for the type value "passed into" the function.
// tslint:disable-next-line:no-unnecessary-generics
export declare function assertType<T>(value: T): void;

46
types/ember/test/mixin.ts Executable file
View File

@ -0,0 +1,46 @@
import Ember from 'ember';
import { assertType } from "./lib/assert";
interface EditableMixin {
edit(): void;
isEditing: boolean;
}
const EditableMixin: Ember.Mixin<EditableMixin, Ember.Route> = Ember.Mixin.create({
edit() {
this.get('controller');
console.log('starting to edit');
this.set('isEditing', true);
},
isEditing: false
});
const EditableComment = Ember.Route.extend(EditableMixin, {
postId: 0,
canEdit() {
return !this.isEditing;
},
tryEdit() {
if (this.canEdit()) {
this.edit();
}
}
});
const comment = EditableComment.create({
postId: 42
});
comment.edit();
comment.canEdit();
comment.tryEdit();
assertType<boolean>(comment.isEditing);
assertType<number>(comment.postId);
const LiteralMixins = Ember.Object.extend({ a: 1 }, { b: 2 }, { c: 3 });
const obj = LiteralMixins.create();
assertType<number>(obj.a);
assertType<number>(obj.b);
assertType<number>(obj.c);

15
types/ember/test/object.ts Executable file
View File

@ -0,0 +1,15 @@
import Ember from 'ember';
const LifetimeHooks = Ember.Object.extend({
resource: null as {} | null,
init() {
this._super();
this.resource = {};
},
willDestroy() {
delete this.resource;
this._super();
}
});

88
types/ember/test/observable.ts Executable file
View File

@ -0,0 +1,88 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
class MyComponent extends Ember.Component {
foo = 'bar';
init() {
this._super.apply(this, arguments);
this.addObserver('foo', this, 'fooDidChange');
this.addObserver('foo', this, this.fooDidChange);
Ember.addObserver(this, 'foo', this, 'fooDidChange');
Ember.addObserver(this, 'foo', this, this.fooDidChange);
this.removeObserver('foo', this, 'fooDidChange');
this.removeObserver('foo', this, this.fooDidChange);
Ember.removeObserver(this, 'foo', this, 'fooDidChange');
Ember.removeObserver(this, 'foo', this, this.fooDidChange);
}
fooDidChange(sender: MyComponent, key: 'foo') {
// your code
}
}
const myComponent = MyComponent.create();
myComponent.addObserver('foo', null, () => {});
myComponent.set('foo', 'baz');
const person = Ember.Object.create({
name: 'Fred',
age: 29,
capitalized: Ember.computed<string>(function() {
return this.get('name').toUpperCase();
})
});
const pojo = { name: 'Fred', age: 29 };
function testGet() {
assertType<string>(Ember.get(person, 'name'));
assertType<number>(Ember.get(person, 'age'));
assertType<string>(Ember.get(person, 'capitalized'));
assertType<string>(person.get('name'));
assertType<number>(person.get('age'));
assertType<string>(person.get('capitalized'));
assertType<string>(Ember.get(pojo, 'name'));
}
function testGetProperties() {
assertType<{ name: string }>(Ember.getProperties(person, 'name'));
assertType<{ name: string, age: number }>(Ember.getProperties(person, 'name', 'age'));
assertType<{ name: string, age: number }>(Ember.getProperties(person, [ 'name', 'age' ]));
assertType<{ name: string, age: number, capitalized: string }>(Ember.getProperties(person, 'name', 'age', 'capitalized'));
assertType<{ name: string }>(person.getProperties('name'));
assertType<{ name: string, age: number }>(person.getProperties('name', 'age'));
assertType<{ name: string, age: number }>(person.getProperties([ 'name', 'age' ]));
assertType<{ name: string, age: number, capitalized: string }>(person.getProperties('name', 'age', 'capitalized'));
assertType<{ name: string, age: number }>(Ember.getProperties(pojo, 'name', 'age'));
}
function testGetWithDefault() {
assertType<string>(Ember.getWithDefault(person, 'name', 'Joe'));
assertType<number>(Ember.getWithDefault(person, 'age', 20));
assertType<string>(Ember.getWithDefault(person, 'capitalized', 'JOE'));
assertType<string>(person.getWithDefault('name', 'Joe'));
assertType<number>(person.getWithDefault('age', 20));
assertType<string>(person.getWithDefault('capitalized', 'JOE'));
assertType<string>(Ember.getWithDefault(pojo, 'name', 'JOE'));
}
function testSet() {
assertType<string>(Ember.set(person, 'name', 'Joe'));
assertType<number>(Ember.set(person, 'age', 35));
assertType<string>(Ember.set(person, 'capitalized', 'JOE'));
assertType<string>(person.set('name', 'Joe'));
assertType<number>(person.set('age', 35));
assertType<string>(person.set('capitalized', 'JOE'));
assertType<string>(Ember.set(pojo, 'name', 'Joe'));
}
function testSetProperties() {
assertType<{ name: string }>(Ember.setProperties(person, { name: 'Joe' }));
assertType<{ name: string, age: number }>(Ember.setProperties(person, { name: 'Joe', age: 35 }));
assertType<{ name: string, capitalized: string }>(Ember.setProperties(person, { name: 'Joe', capitalized: 'JOE' }));
assertType<{ name: string }>(person.setProperties({ name: 'Joe' }));
assertType<{ name: string, age: number }>(person.setProperties({ name: 'Joe', age: 35 }));
assertType<{ name: string, capitalized: string }>(person.setProperties({ name: 'Joe', capitalized: 'JOE' }));
assertType<{ name: string, age: number }>(Ember.setProperties(pojo, { name: 'Joe', age: 35 }));
}

65
types/ember/test/reopen.ts Executable file
View File

@ -0,0 +1,65 @@
import Ember from 'ember';
import { assertType } from "./lib/assert";
type Person = typeof Person.prototype;
const Person = Ember.Object.extend({
name: '',
sayHello() {
alert(`Hello. My name is ${this.get('name')}`);
}
});
assertType<Person>(Person.reopen());
assertType<string>(Person.create().name);
assertType<void>(Person.create().sayHello());
const Person2 = Person.reopenClass({
species: 'Homo sapiens',
createPerson(name: string): Person {
return Person.create({ name });
}
});
assertType<string>(Person2.create().name);
assertType<void>(Person2.create().sayHello());
assertType<string>(Person2.species);
let tom = Person2.create({
name: 'Tom Dale'
});
let yehuda = Person2.createPerson('Yehuda Katz');
tom.sayHello(); // "Hello. My name is Tom Dale"
yehuda.sayHello(); // "Hello. My name is Yehuda Katz"
alert(Person2.species); // "Homo sapiens"
const Person3 = Person2.reopen({
goodbyeMessage: 'goodbye',
sayGoodbye() {
alert(`${this.get('goodbyeMessage')}, ${this.get('name')}`);
}
});
const person3 = Person3.create();
person3.get('name');
person3.get('goodbyeMessage');
person3.sayHello();
person3.sayGoodbye();
interface AutoResizeMixin { resizable: true; }
declare const AutoResizeMixin: Ember.Mixin<AutoResizeMixin>;
const ResizableTextArea = Ember.TextArea.reopen(AutoResizeMixin, {
scaling: 1.0
});
const text = ResizableTextArea.create();
assertType<boolean>(text.resizable);
assertType<number>(text.scaling);
const Reopened = Ember.Object.reopenClass({ a: 1 }, { b: 2 }, { c: 3 });
assertType<number>(Reopened.a);
assertType<number>(Reopened.b);
assertType<number>(Reopened.c);

87
types/ember/test/route.ts Executable file
View File

@ -0,0 +1,87 @@
import Route from '@ember/routing/route';
import Object from '@ember/object';
import Array from '@ember/array';
import Ember from 'ember'; // currently needed for Transition
interface Post extends Ember.Object {}
interface Posts extends Array<Post> {}
Route.extend({
beforeModel(transition: Ember.Transition) {
this.transitionTo('someOtherRoute');
},
});
Route.extend({
afterModel(posts: Posts, transition: Ember.Transition) {
if (posts.length === 1) {
this.transitionTo('post.show', posts.firstObject);
}
},
});
Route.extend({
actions: {
showModal(evt: { modalName: string }) {
this.render(evt.modalName, {
outlet: 'modal',
into: 'application',
});
},
hideModal(evt: { modalName: string }) {
this.disconnectOutlet({
outlet: 'modal',
parentView: 'application',
});
},
},
});
Ember.Route.extend({
model() {
return this.modelFor('post');
},
});
Route.extend({
queryParams: {
memberQp: { refreshModel: true },
},
});
Route.extend({
renderTemplate() {
this.render('photos', {
into: 'application',
outlet: 'anOutletName',
});
},
});
Route.extend({
renderTemplate(controller: Ember.Controller, model: {}) {
this.render('posts', {
view: 'someView', // the template to render, referenced by name
into: 'application', // the template to render into, referenced by name
outlet: 'anOutletName', // the outlet inside `options.into` to render into.
controller: 'someControllerName', // the controller to use for this template, referenced by name
model, // the model to set on `options.controller`.
});
},
});
Route.extend({
resetController(controller: Ember.Controller, isExiting: boolean, transition: boolean) {
if (isExiting) {
// controller.set('page', 1);
}
},
});
Route.extend({
setupController(controller: Ember.Controller, model: {}) {
this._super(controller, model);
this.controllerFor('application').set('model', model);
},
});

23
types/ember/test/router.ts Executable file
View File

@ -0,0 +1,23 @@
import Ember from 'ember';
const AppRouter = Ember.Router.extend({
});
AppRouter.map(function() {
this.route('index', { path: '/' });
this.route('about');
this.route('favorites', { path: '/favs' });
this.route('posts', function() {
this.route('index', { path: '/' });
this.route('new');
this.route('post', { path: '/post/:post_id', resetNamespace: true });
this.route('comments', { resetNamespace: true }, function() {
this.route('new');
});
});
this.route('photo', { path: '/photo/:id' }, function() {
this.route('comment', { path: '/comment/:id' });
});
this.route('not-found', { path: '/*path' });
this.mount('my-engine');
});

204
types/ember/test/run.ts Executable file
View File

@ -0,0 +1,204 @@
import Ember from 'ember';
import RSVP from 'rsvp';
import { run } from '@ember/runloop';
import { assertType } from "./lib/assert";
assertType<string[]>(Ember.run.queues);
function testRun() {
let r = run(function() {
// code to be executed within a RunLoop
return 123;
});
assertType<number>(r);
function destroyApp(application: Ember.Application) {
Ember.run(application, 'destroy');
run(application, function() {
this.destroy();
});
}
}
function testBind() {
Ember.Component.extend({
init() {
const bound = Ember.run.bind(this, this.setupEditor);
bound();
},
editor: null as string | null,
setupEditor(editor: string) {
this.set('editor', editor);
}
});
}
function testCancel() {
const myContext = {};
let runNext = run.next(myContext, function() {
// will not be executed
});
run.cancel(runNext);
let runLater = run.later(myContext, function() {
// will not be executed
}, 500);
run.cancel(runLater);
let runScheduleOnce = run.scheduleOnce('afterRender', myContext, function() {
// will not be executed
});
run.cancel(runScheduleOnce);
let runOnce = run.once(myContext, function() {
// will not be executed
});
run.cancel(runOnce);
let throttle = run.throttle(myContext, function() {
// will not be executed
}, 1, false);
run.cancel(throttle);
let debounce = run.debounce(myContext, function() {
// will not be executed
}, 1);
run.cancel(debounce);
let debounceImmediate = run.debounce(myContext, function() {
// will be executed since we passed in true (immediate)
}, 100, true);
// the 100ms delay until this method can be called again will be canceled
run.cancel(debounceImmediate);
}
function testDebounce() {
function runIt() {
}
let myContext = { name: 'debounce' };
run.debounce(runIt, 150);
run.debounce(myContext, runIt, 150);
run.debounce(myContext, runIt, 150, true);
Ember.Component.extend({
searchValue: 'test',
fetchResults(value: string) {},
actions: {
handleTyping() {
// the fetchResults function is passed into the component from its parent
Ember.run.debounce(this, this.get('fetchResults'), this.get('searchValue'), 250);
}
}
});
}
function testBegin() {
run.begin();
// code to be executed within a RunLoop
run.end();
}
function testJoin() {
run.join(function() {
// creates a new run-loop
});
run(function() {
// creates a new run-loop
run.join(function() {
// joins with the existing run-loop, and queues for invocation on
// the existing run-loops action queue.
});
});
new RSVP.Promise(function(resolve) {
Ember.run.later(function() {
resolve({ msg: 'Hold Your Horses' });
}, 3000);
});
}
function testLater() {
const myContext = {};
run.later(myContext, function() {
// code here will execute within a RunLoop in about 500ms with this == myContext
}, 500);
}
function testNext() {
const myContext = {};
run.next(myContext, function() {
// code to be executed in the next run loop,
// which will be scheduled after the current one
});
}
function testOnce() {
Ember.Component.extend({
init() {
Ember.run.once(this, 'processFullName');
},
processFullName() {
}
});
}
function testSchedule() {
Ember.Component.extend({
init() {
run.schedule('sync', this, function() {
// this will be executed in the first RunLoop queue, when bindings are synced
console.log('scheduled on sync queue');
});
run.schedule('actions', this, function() {
// this will be executed in the 'actions' queue, after bindings have synced.
console.log('scheduled on actions queue');
});
}
});
Ember.run.schedule('actions', () => {
// Do more things
});
}
function testScheduleOnce() {
function sayHi() {
console.log('hi');
}
const myContext = {};
run(function() {
run.scheduleOnce('afterRender', myContext, sayHi);
run.scheduleOnce('afterRender', myContext, sayHi);
// sayHi will only be executed once, in the afterRender queue of the RunLoop
});
run.scheduleOnce('actions', myContext, function() {
console.log('Closure');
});
}
function testThrottle() {
function runIt() {
}
let myContext = { name: 'throttle' };
run.throttle(runIt, 150);
run.throttle(myContext, runIt, 150);
}

32
types/ember/test/test.ts Executable file
View File

@ -0,0 +1,32 @@
import Ember from 'ember';
let pending = 0;
Ember.Test.registerWaiter(() => pending !== 0);
declare const MyDb: {
hasPendingTransactions(): boolean;
};
Ember.Test.registerWaiter(MyDb, MyDb.hasPendingTransactions);
Ember.Test.promise(function(resolve) {
window.setTimeout(resolve, 500);
});
Ember.Test.registerHelper('boot', function(app) {
Ember.run(app, app.advanceReadiness);
});
Ember.Test.registerAsyncHelper('boot', function(app) {
Ember.run(app, app.advanceReadiness);
});
Ember.Test.registerAsyncHelper('waitForPromise', (app, promise) => {
return new Ember.Test.Promise((resolve) => {
Ember.Test.adapter.asyncStart();
promise.then(() => {
Ember.run.schedule('afterRender', null, resolve);
Ember.Test.adapter.asyncEnd();
});
});
});

28
types/ember/test/transition.ts Executable file
View File

@ -0,0 +1,28 @@
import Ember from 'ember';
Ember.Route.extend({
beforeModel(transition: Ember.Transition) {
if (new Date() > new Date('January 1, 1980')) {
alert('Sorry, you need a time machine to enter this route.');
transition.abort();
}
}
});
Ember.Controller.extend({
previousTransition: <Ember.Transition | null> null,
actions: {
login() {
// Log the user in, then reattempt previous transition if it exists.
let previousTransition = this.get('previousTransition');
if (previousTransition) {
this.set('previousTransition', null);
previousTransition.retry();
} else {
// Default back to homepage
this.transitionToRoute('index');
}
}
}
});

71
types/ember/test/utils.ts Executable file
View File

@ -0,0 +1,71 @@
import Ember from 'ember';
import * as utils from '@ember/utils';
import { assertType } from "./lib/assert";
function testIsNoneType() {
const maybeUndefined: string | undefined = 'not actually undefined';
if (utils.isNone(maybeUndefined)) {
return;
}
const anotherString = maybeUndefined + 'another string';
}
function testMerge() {
assertType<{ first: string, last: string }>(
Ember.merge({ first: 'Tom' }, { last: 'Dale' })
);
}
function testAssign() {
assertType<{ first: string, middle: string, last: string }>(
Ember.assign({ first: 'Tom' }, { middle: 'M' }, { last: 'Dale' })
);
}
function testOnError() {
Ember.onerror = function(error) {
Ember.$.post('/report-error', {
stack: error.stack,
otherInformation: 'whatever app state you want to provide'
});
};
}
function testMakeArray() {
assertType<any[]>(Ember.makeArray());
assertType<any[]>(Ember.makeArray(null));
assertType<any[]>(Ember.makeArray(undefined));
assertType<string[]>(Ember.makeArray('lindsay'));
assertType<number[]>(Ember.makeArray([1, 2, 42]));
}
function testDeprecateFunc() {
function newMethod(first: string, second: number): string {
return '';
}
let oldMethod = Ember.deprecateFunc('Please use the new method', { id: 'deprecated.id', until: '6.0' }, newMethod);
assertType<string>(newMethod('first', 123));
assertType<string>(oldMethod('first', 123));
}
function testDefineProperty() {
const contact = {};
// ES5 compatible mode
Ember.defineProperty(contact, 'firstName', {
writable: true,
configurable: false,
enumerable: true,
value: 'Charles'
});
// define a simple property
Ember.defineProperty(contact, 'lastName', undefined, 'Jolley');
// define a computed property
Ember.defineProperty(contact, 'fullName', Ember.computed('firstName', 'lastName', function() {
return `${this.firstName} ${this.lastName}`;
}));
}

39
types/ember/tsconfig.json Normal file → Executable file
View File

@ -1,24 +1,49 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": false,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictFunctionTypes": false,
"baseUrl": "../",
"typeRoots": [
"../"
],
"typeRoots": ["../"],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ember-tests.ts"
"test/lib/assert.ts",
"test/application.ts",
"test/ember-tests.ts",
"test/event.ts",
"test/extend.ts",
"test/create.ts",
"test/object.ts",
"test/observable.ts",
"test/mixin.ts",
"test/reopen.ts",
"test/detect.ts",
"test/detect-instance.ts",
"test/array.ts",
"test/array-ext.ts",
"test/array-proxy.ts",
"test/helper.ts",
"test/computed.ts",
"test/component.ts",
"test/function-ext.ts",
"test/inject.ts",
"test/utils.ts",
"test/transition.ts",
"test/router.ts",
"test/run.ts",
"test/test.ts",
"test/controller.ts",
"test/route.ts"
]
}
}

21
types/ember/tslint.json Normal file → Executable file
View File

@ -4,11 +4,24 @@
// Heavy use of Function type in this older package.
"ban-types": false,
"jsdoc-format": false,
"no-any-union": false,
"no-misused-new": false,
// not sure what this means
// these are disabled because of rfc176 module exports
"strict-export-declare-modifiers": false,
"no-single-declare-module": false,
"no-unnecessary-generics": false,
"no-unnecessary-qualifier": false
"no-declare-current-package": false,
"no-self-import": false,
// We use interfaces in a number of places to express things (including
// mixins in particular, but also including extending a global
// interface) which TS currently can't express correctly.
"no-empty-interface": false,
"no-duplicate-imports": false,
"no-unnecessary-qualifier": false,
"prefer-const": false,
"no-void-expression": false,
"only-arrow-functions": false,
"no-submodule-imports": false
}
}

961
types/rsvp/index.d.ts vendored Normal file → Executable file
View File

@ -1,382 +1,629 @@
// Type definitions for RSVP 3.3.3
// Type definitions for RSVP 4.0
// Project: https://github.com/tildeio/rsvp.js
// Definitions by: Taylor Brown <https://github.com/Taytay>
// Mikael Kohlmyr <https://github.com/mkohlmyr>
// Theron Cross <https://github.com/theroncross>
// Chris Krycho <https://github.com/chriskrycho>
// Definitions by: Chris Krycho <https://github.com/chriskrycho>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// Some of this file was taken from the type definitions for es6-promise https://github.com/borisyankov/DefinitelyTyped/blob/master/es6-promise/es6-promise.d.ts
// Credit for that file goes to: François de Campredon <https://github.com/fdecampredon>
// These types are derived in large part from the Microsoft-supplied types for
// ES2015 Promises. They have been tweaked to support RSVP's extensions to the
// Promises A+ spec and the additional helper functions it supplies.
// Some of this file was taken from the type definitions for Q : https://github.com/borisyankov/DefinitelyTyped/blob/master/q/Q.d.ts
// Credit for that file goes to: Barrie Nemetchek <https://github.com/bnemetchek>, Andrew Gaspar <https://github.com/AndrewGaspar>, John Reilly <https://github.com/johnnyreilly>
declare module 'rsvp' {
namespace RSVP {
// All the Promise methods essentially flatten existing promises, so that
// you don't end up with `Promise<Promise<Promise<string>>>` if you happen
// to return another `Promise` from a `.then()` invocation, etc. So all of
// them can take a type or a promise-like/then-able type.
type Arg<T> = T | PromiseLike<T>;
declare namespace RSVP {
type Resolution<T, U, C> = (value: T) => U | Thenable<U, C>;
type Rejection<T, C, D> = (error: C) => D | Thenable<T, D>;
// RSVP supplies status for promises in certain places.
enum State {
fulfilled = 'fulfilled',
rejected = 'rejected',
pending = 'pending',
}
interface Thenable<T, C> {
then(label?: string): Thenable<T, C>;
then<U>(onFulfillment: Resolution<T, U, C>, label?: string): Thenable<U, C>;
then<U, D>(
onFulfillment: Resolution<T, U, C>,
onRejected: Rejection<T, C, D>,
label?: string
): Thenable<U, D>;
}
type Resolved<T> = {
state: State.fulfilled;
value: T;
};
interface Catchable<C> {
catch(label?: string): Catchable<C>;
catch<D>(onRejection: (error: C) => D, label?: string): Catchable<D>;
}
type Rejected<T = any> = {
state: State.rejected;
reason: T;
};
interface Deferred<T, C> {
promise: Promise<T, C>;
resolve(value: T): void;
reject(reason: C): void;
}
type Pending = {
state: State.pending;
};
type PromiseStates = 'fulfilled' | 'rejected' | 'pending';
interface IPromiseState<T, C> {
state: PromiseStates;
value: T;
reason: C;
}
type PromiseState<T> = Resolved<T> | Rejected | Pending;
class Resolved<T, C> implements IPromiseState<T, C> {
state: 'fulfilled';
value: T;
reason: never;
}
type Deferred<T> = {
promise: Promise<T>;
resolve: (value?: RSVP.Arg<T>) => void;
reject: (reason?: any) => void;
};
class Rejected<T, C> implements IPromiseState<T, C> {
state: 'rejected';
value: never;
reason: C;
}
interface InstrumentEvent {
guid: string; // guid of promise. Must be globally unique, not just within the implementation
childGuid: string; // child of child promise (for chained via `then`)
eventName: string; // one of ['created', 'chained', 'fulfilled', 'rejected']
detail: any; // fulfillment value or rejection reason, if applicable
label: string; // label passed to promise's constructor
timeStamp: number; // milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now
}
class Pending<T, C> implements IPromiseState<T, C> {
state: 'pending';
value: never;
reason: never;
}
interface ObjectWithEventMixins {
on(
eventName: 'created' | 'chained' | 'fulfilled' | 'rejected',
listener: (event: InstrumentEvent) => void
): void;
on(eventName: 'error', errorHandler: (reason: any) => void): void;
on(eventName: string, callback: (value: any) => void): void;
off(eventName: string, callback?: (value: any) => void): void;
trigger(eventName: string, options?: any, label?: string): void;
}
type PromiseState<T, C> = Resolved<T, C> | Rejected<C, C> | Pending<T, C>;
class EventTarget {
/** `RSVP.EventTarget.mixin` extends an object with EventTarget methods. */
static mixin(object: object): ObjectWithEventMixins;
type PromiseHash<T, C> = { [P in keyof T]: Thenable<T[P], C> | T[P] };
/** Registers a callback to be executed when `eventName` is triggered */
static on(
eventName: 'created' | 'chained' | 'fulfilled' | 'rejected',
listener: (event: InstrumentEvent) => void
): void;
static on(eventName: 'error', errorHandler: (reason: any) => void): void;
static on(eventName: string, callback: (value: any) => void): void;
type SettledHash<T, C> = { [P in keyof T]: PromiseState<T[P], C> };
interface InstrumentEvent {
guid: string; // guid of promise. Must be globally unique, not just within the implementation
childGuid: string; // child of child promise (for chained via `then`)
eventName: string; // one of ['created', 'chained', 'fulfilled', 'rejected']
detail: any; // fulfillment value or rejection reason, if applicable
label: string; // label passed to promise's constructor
timeStamp: number; // milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now
}
interface ObjectWithEventMixins {
on(
eventName: 'created' | 'chained' | 'fulfilled' | 'rejected',
listener: (event: InstrumentEvent) => void
): void;
on(eventName: 'error', errorHandler: (reason: any) => void): void;
on(eventName: string, callback: (value: any) => void): void;
off(eventName: string, callback?: (value: any) => void): void;
trigger(eventName: string, options?: any, label?: string): void;
}
class Promise<T, C> implements Thenable<T, C>, Catchable<C> {
/**
* If you call resolve in the body of the callback passed to the constructor,
* your promise is fulfilled with result object passed to resolve.
* If you call reject your promise is rejected with the object passed to reject.
* For consistency and debugging (eg stack traces), obj should be an instanceof Error.
* Any errors thrown in the constructor callback will be implicitly passed to reject().
*/
constructor(
callback: (
resolve: (result?: T | Thenable<T, never>) => void,
reject: (error: C | Thenable<never, C>) => void
) => void,
label?: string
);
/**
* onFulfillment is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
* Both are optional, if either/both are omitted the next onFulfillment/onRejected in the chain is called.
* Both callbacks have a single parameter , the fulfillment value or rejection reason.
* "then" returns a new promise equivalent to the value you return from onFulfillment/onRejected after being passed through Promise.resolve.
* If an error is thrown in the callback, the returned promise rejects with that error.
*
* @param onFulfillment called when/if "promise" resolves
* @param onRejected called when/if "promise" rejects
* @param label useful for tooling
*/
then<U, D>(
onFulfillment: Resolution<T, U, C>,
onRejected: Rejection<T, C, D>,
label?: string
): Promise<U, D>;
then<U>(onFulfillment: Resolution<T, U, C>, label?: string): Promise<U, C>;
then(label?: string): Promise<T, C>;
/**
* Sugar for promise.then(undefined, onRejected)
*/
catch(label?: string): Promise<T, C>;
catch<D>(onRejection: Rejection<T, C, D>, label?: string): Promise<T, D>;
finally(finallyCallback: Function): Promise<T, C>;
/**
* `RSVP.Promise.all` accepts an array of promises, and returns a new promise which
* is fulfilled with an array of fulfillment values for the passed promises, or
* rejected with the reason of the first passed promise to be rejected. It casts all
* elements of the passed iterable to promises as it runs this algorithm.
/**
* You can use `off` to stop firing a particular callback for an event.
*
* If you don't pass a `callback` argument to `off`, ALL callbacks for the
* event will not be executed when the event fires.
*/
static all<T, C>(promises: Thenable<T, C>[], label?: string): Promise<T[], C>;
static off(eventName: string, callback?: (value: any) => void): void;
/**
* `RSVP.Promise.race` returns a new promise which is settled in the same way as the
* first passed promise to settle.
*
* `RSVP.Promise.race` is deterministic in that only the state of the first
* settled promise matters. For example, even if other promises given to the
* `promises` array argument are resolved, but the first settled promise has
* become rejected before the other promises became fulfilled, the returned
* promise will become rejected.
*/
static race<T, C>(promises: Promise<T, C>[]): Promise<T, C>;
/**
* Use `trigger` to fire custom events.
*
* You can also pass a value as a second argument to `trigger` that will be
* passed as an argument to all event listeners for the event
*/
static trigger(eventName: string, options?: any, label?: string): void;
}
/**
* Returns a promise that will become resolved with the passed `value`
*/
static resolve<T>(value: T, label?: string): Promise<T, never>;
class Promise<T> implements PromiseLike<T> {
constructor(
executor: (
resolve: (value?: RSVP.Arg<T>) => void,
reject: (reason?: any) => void
) => void
);
/**
* Deprecated in favor of resolve
*/
static cast<T>(value: T, label?: string): Promise<T, never>;
new<T>(
executor: (
resolve: (value?: RSVP.Arg<T>) => void,
reject: (reason?: any) => void
) => void
): RSVP.Promise<T>;
/**
* Returns a promise rejected with the passed `reason`.
*/
static reject<C>(reason: C): Promise<never, C>;
then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
label?: string
): RSVP.Promise<TResult1 | TResult2>;
catch<TResult = never>(
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
label?: string
): RSVP.Promise<T | TResult>;
finally<U>(onFinally?: U | PromiseLike<U>): RSVP.Promise<T>;
static all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
values: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>,
Arg<T10>
],
label?: string
): RSVP.Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
static all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
values: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>
],
label?: string
): RSVP.Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
static all<T1, T2, T3, T4, T5, T6, T7, T8>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>, Arg<T8>],
label?: string
): RSVP.Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
static all<T1, T2, T3, T4, T5, T6, T7>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>],
label?: string
): RSVP.Promise<[T1, T2, T3, T4, T5, T6, T7]>;
static all<T1, T2, T3, T4, T5, T6>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>],
label?: string
): RSVP.Promise<[T1, T2, T3, T4, T5, T6]>;
static all<T1, T2, T3, T4, T5>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>],
label?: string
): RSVP.Promise<[T1, T2, T3, T4, T5]>;
static all<T1, T2, T3, T4>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>],
label?: string
): RSVP.Promise<[T1, T2, T3, T4]>;
static all<T1, T2, T3>(
values: [Arg<T1>, Arg<T2>, Arg<T3>],
label?: string
): RSVP.Promise<[T1, T2, T3]>;
static all<T1, T2>(values: [Arg<T1>, Arg<T2>], label?: string): Promise<[T1, T2]>;
static all<T>(values: (Arg<T>)[], label?: string): RSVP.Promise<T[]>;
static race<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
values: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>,
T10 | PromiseLike<T10>
],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
static race<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
values: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>
],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
static race<T1, T2, T3, T4, T5, T6, T7, T8>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>, Arg<T8>],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
static race<T1, T2, T3, T4, T5, T6, T7>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7>;
static race<T1, T2, T3, T4, T5, T6>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4 | T5 | T6>;
static race<T1, T2, T3, T4, T5>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4 | T5>;
static race<T1, T2, T3, T4>(
values: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>],
label?: string
): RSVP.Promise<T1 | T2 | T3 | T4>;
static race<T1, T2, T3>(
values: [Arg<T1>, Arg<T2>, Arg<T3>],
label?: string
): RSVP.Promise<T1 | T2 | T3>;
static race<T1, T2>(values: [Arg<T1>, Arg<T2>], label?: string): RSVP.Promise<T1 | T2>;
static race<T>(values: (Arg<T>)[], label?: string): RSVP.Promise<T>;
static reject(reason?: any, label?: string): RSVP.Promise<never>;
static resolve<T>(value?: Arg<T>, label?: string): RSVP.Promise<T>;
static resolve(): RSVP.Promise<void>;
/**
* @deprecated
*/
static cast: typeof RSVP.Promise.resolve;
}
const all: typeof Promise.all;
const race: typeof Promise.race;
const reject: typeof Promise.reject;
const resolve: typeof Promise.resolve;
function rethrow(reason: any): void;
const cast: typeof Promise.cast;
const on: typeof EventTarget.on;
const off: typeof EventTarget.off;
// ----- denodeify ----- //
// Here be absurd things because we don't have variadic types. All of
// this will go away if we can ever write this:
//
// denodeify<...T, ...A>(
// nodeFunc: (...args: ...A, callback: (err: any, ...cbArgs: ...T) => any) => void,
// options?: false
// ): (...args: ...A) => RSVP.Promise<...T>
//
// That day, however, may never come. So, in the meantime, we do this.
function denodeify<T1, T2, T3, A>(
nodeFunc: (
arg1: A,
callback: (err: any, data1: T1, data2: T2, data3: T3) => void
) => void,
options?: false
): (arg1: A) => RSVP.Promise<T1>;
function denodeify<T1, T2, A>(
nodeFunc: (arg1: A, callback: (err: any, data1: T1, data2: T2) => void) => void,
options?: false
): (arg1: A) => RSVP.Promise<T1>;
function denodeify<T, A>(
nodeFunc: (arg1: A, callback: (err: any, data: T) => void) => void,
options?: false
): (arg1: A) => RSVP.Promise<T>;
function denodeify<T1, T2, T3, A>(
nodeFunc: (
arg1: A,
callback: (err: any, data1: T1, data2: T2, data3: T3) => void
) => void,
options: true
): (arg1: A) => RSVP.Promise<[T1, T2, T3]>;
function denodeify<T1, T2, A>(
nodeFunc: (arg1: A, callback: (err: any, data1: T1, data2: T2) => void) => void,
options: true
): (arg1: A) => RSVP.Promise<[T1, T2]>;
function denodeify<T, A>(
nodeFunc: (arg1: A, callback: (err: any, data: T) => void) => void,
options: true
): (arg1: A) => RSVP.Promise<[T]>;
function denodeify<T1, T2, T3, A, K1 extends string, K2 extends string, K3 extends string>(
nodeFunc: (
arg1: A,
callback: (err: any, data1: T1, data2: T2, data3: T3) => void
) => void,
options: [K1, K2, K3]
): (arg1: A) => RSVP.Promise<{ [K in K1]: T1 } & { [K in K2]: T2 } & { [K in K3]: T3 }>;
function denodeify<T1, T2, A, K1 extends string, K2 extends string>(
nodeFunc: (arg1: A, callback: (err: any, data1: T1, data2: T2) => void) => void,
options: [K1, K2]
): (arg1: A) => RSVP.Promise<{ [K in K1]: T1 } & { [K in K2]: T2 }>;
function denodeify<T, A, K1 extends string>(
nodeFunc: (arg1: A, callback: (err: any, data: T) => void) => void,
options: [K1]
): (arg1: A) => RSVP.Promise<{ [K in K1]: T }>;
// ----- hash and hashSettled ----- //
function hash<T>(object: { [P in keyof T]: Arg<T[P]> }, label?: string): RSVP.Promise<T>;
function hashSettled<T>(
object: { [P in keyof T]: Arg<T[P]> },
label?: string
): RSVP.Promise<{ [P in keyof T]: PromiseState<T[P]> }>;
function allSettled<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
entries: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>,
Arg<T10>
],
label?: string
): RSVP.Promise<
[
PromiseState<T1>,
PromiseState<T2>,
PromiseState<T3>,
PromiseState<T4>,
PromiseState<T5>,
PromiseState<T6>,
PromiseState<T7>,
PromiseState<T8>,
PromiseState<T9>
]
>;
function allSettled<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
entries: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>
],
label?: string
): RSVP.Promise<
[
PromiseState<T1>,
PromiseState<T2>,
PromiseState<T3>,
PromiseState<T4>,
PromiseState<T5>,
PromiseState<T6>,
PromiseState<T7>,
PromiseState<T8>,
PromiseState<T9>
]
>;
function allSettled<T1, T2, T3, T4, T5, T6, T7, T8>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>, Arg<T8>],
label?: string
): RSVP.Promise<
[
PromiseState<T1>,
PromiseState<T2>,
PromiseState<T3>,
PromiseState<T4>,
PromiseState<T5>,
PromiseState<T6>,
PromiseState<T7>,
PromiseState<T8>
]
>;
function allSettled<T1, T2, T3, T4, T5, T6, T7>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>],
label?: string
): RSVP.Promise<
[
PromiseState<T1>,
PromiseState<T2>,
PromiseState<T3>,
PromiseState<T4>,
PromiseState<T5>,
PromiseState<T6>,
PromiseState<T7>
]
>;
function allSettled<T1, T2, T3, T4, T5, T6>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>],
label?: string
): RSVP.Promise<
[
PromiseState<T1>,
PromiseState<T2>,
PromiseState<T3>,
PromiseState<T4>,
PromiseState<T5>,
PromiseState<T6>
]
>;
function allSettled<T1, T2, T3, T4, T5>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>],
label?: string
): RSVP.Promise<
[
PromiseState<T1>,
PromiseState<T2>,
PromiseState<T3>,
PromiseState<T4>,
PromiseState<T5>
]
>;
function allSettled<T1, T2, T3, T4>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>],
label?: string
): RSVP.Promise<[PromiseState<T1>, PromiseState<T2>, PromiseState<T3>, PromiseState<T4>]>;
function allSettled<T1, T2, T3>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>],
label?: string
): RSVP.Promise<[PromiseState<T1>, PromiseState<T2>, PromiseState<T3>]>;
function allSettled<T1, T2>(
entries: [Arg<T1>, Arg<T2>],
label?: string
): RSVP.Promise<[PromiseState<T1>, PromiseState<T2>]>;
function allSettled<T>(entries: Arg<T>[], label?: string): RSVP.Promise<[PromiseState<T>]>;
function map<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, U>(
entries: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>,
Arg<T10>
],
mapFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 10 }>;
function map<T1, T2, T3, T4, T5, T6, T7, T8, T9, U>(
entries: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>
],
mapFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 9 }>;
function map<T1, T2, T3, T4, T5, T6, T7, T8, U>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>, Arg<T8>],
mapFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 8 }>;
function map<T1, T2, T3, T4, T5, T6, T7, U>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>],
mapFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 7 }>;
function map<T1, T2, T3, T4, T5, T6, U>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>],
mapFn: (item: T1 | T2 | T3 | T4 | T5 | T6) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 6 }>;
function map<T1, T2, T3, T4, T5, U>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>],
mapFn: (item: T1 | T2 | T3 | T4 | T5) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 5 }>;
function map<T1, T2, T3, T4, U>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>],
mapFn: (item: T1 | T2 | T3 | T4) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 4 }>;
function map<T1, T2, T3, U>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>],
mapFn: (item: T1 | T2 | T3) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 3 }>;
function map<T1, T2, U>(
entries: [Arg<T1>, Arg<T2>],
mapFn: (item: T1 | T2) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 2 }>;
function map<T, U>(
entries: Arg<T>[],
mapFn: (item: T) => U,
label?: string
): RSVP.Promise<Array<U> & { length: 1 }>;
function filter<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
entries: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>,
Arg<T10>
],
filterFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>>;
function filter<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
entries: [
Arg<T1>,
Arg<T2>,
Arg<T3>,
Arg<T4>,
Arg<T5>,
Arg<T6>,
Arg<T7>,
Arg<T8>,
Arg<T9>
],
filterFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>>;
function filter<T1, T2, T3, T4, T5, T6, T7, T8>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>, Arg<T8>],
filterFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>>;
function filter<T1, T2, T3, T4, T5, T6, T7>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>, Arg<T7>],
filterFn: (item: T1 | T2 | T3 | T4 | T5 | T6 | T7) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4 | T5 | T6 | T7>>;
function filter<T1, T2, T3, T4, T5, T6>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>, Arg<T6>],
filterFn: (item: T1 | T2 | T3 | T4 | T5 | T6) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4 | T5 | T6> & { length: 6 }>;
function filter<T1, T2, T3, T4, T5>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>, Arg<T5>],
filterFn: (item: T1 | T2 | T3 | T4 | T5) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4 | T5>>;
function filter<T1, T2, T3, T4>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>, Arg<T4>],
filterFn: (item: T1 | T2 | T3 | T4) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3 | T4>>;
function filter<T1, T2, T3>(
entries: [Arg<T1>, Arg<T2>, Arg<T3>],
filterFn: (item: T1 | T2 | T3) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2 | T3>>;
function filter<T1, T2>(
entries: [Arg<T1>, Arg<T2>],
filterFn: (item: T1 | T2) => boolean,
label?: string
): RSVP.Promise<Array<T1 | T2>>;
function filter<T>(
entries: Arg<T>[],
filterFn: (item: T) => boolean,
label?: string
): RSVP.Promise<Array<T>>;
function defer<T>(label?: string): Deferred<T>;
function configure<T>(name: string): T;
function configure<T>(name: string, value: T): void;
function asap<T, U>(callback: (callbackArg: T) => U, arg: T): void;
const async: typeof asap;
}
export namespace EventTarget {
/** `RSVP.EventTarget.mixin` extends an object with EventTarget methods. */
function mixin(object: object): ObjectWithEventMixins;
export default RSVP;
/** Registers a callback to be executed when `eventName` is triggered */
function on(
eventName: 'created' | 'chained' | 'fulfilled' | 'rejected',
listener: (event: InstrumentEvent) => void
): void;
function on(eventName: 'error', errorHandler: (reason: any) => void): void;
function on(eventName: string, callback: (value: any) => void): void;
/**
* You can use `off` to stop firing a particular callback for an event.
*
* If you don't pass a `callback` argument to `off`, ALL callbacks for the
* event will not be executed when the event fires.
*/
function off(eventName: string, callback?: (value: any) => void): void;
/**
* Use `trigger` to fire custom events.
*
* You can also pass a value as a second argument to `trigger` that will be
* passed as an argument to all event listeners for the event
*/
function trigger(eventName: string, options?: any, label?: string): void;
}
export function configure(
configName: 'instrument' | 'instrument-with-stack',
shouldInstrument: boolean
): void;
export function configure(configName: string, value: any): void;
/**
* Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
* the array passed to all can be a mixture of promise-like objects and other objects.
* The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
*/
export function all<T, C>(promises: Thenable<T, C>[]): Promise<T[], C>;
/**
* `RSVP.hash` is similar to `RSVP.all`, but takes an object instead of an array
* for its `promises` argument.
*
* Returns a promise that is fulfilled when all the given promises have been
* fulfilled, or rejected if any of them become rejected. The returned promise
* is fulfilled with a hash that has the same key names as the `promises` object
* argument. If any of the values in the object are not promises, they will
* simply be copied over to the fulfilled object.
*
* If any of the `promises` given to `RSVP.hash` are rejected, the first promise
* that is rejected will be given as the reason to the rejection handler.
*/
export function hash<T, C>(promises: PromiseHash<T, C>): Promise<T, C>;
/**
* `RSVP.map` is similar to JavaScript's native `map` method. `mapFn` is eagerly called
* meaning that as soon as any promise resolves its value will be passed to `mapFn`.
* `RSVP.map` returns a promise that will become fulfilled with the result of running
* `mapFn` on the values the promises become fulfilled with.
*
* If any of the `promises` given to `RSVP.map` are rejected, the first promise
* that is rejected will be given as an argument to the returned promise's
* rejection handler.
*/
export function map<T, U, C>(
promises: Thenable<T, C>[],
mapFn: (item: T) => U,
label?: string
): Promise<U[], C>;
/**
* `RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing
* a fail-fast method, it waits until all the promises have returned and
* shows you all the results. This is useful if you want to handle multiple
* promises' failure states together as a set.
*/
export function allSettled<T, C>(promises: Thenable<T, C>[]): Promise<PromiseState<T, C>[], C>;
/**
* `RSVP.hashSettled` is similar to `RSVP.allSettled`, but takes an object
* instead of an array for its `promises` argument.
*
* Unlike `RSVP.all` or `RSVP.hash`, which implement a fail-fast method,
* but like `RSVP.allSettled`, `hashSettled` waits until all the
* constituent promises have returned and then shows you all the results
* with their states and values/reasons. This is useful if you want to
* handle multiple promises' failure states together as a set.
*/
export function hashSettled<T, C>(promises: PromiseHash<T, C>): Promise<SettledHash<T, C>, C>;
/**
* Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
*/
function race<T, C>(promises: Promise<T, C>[]): Promise<T, C>;
/**
* `RSVP.denodeify` takes a "node-style" function and returns a function that
* will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the
* browser when you'd prefer to use promises over using callbacks. For example,
* `denodeify` transforms the following:
*
* ```
* let fs = require('fs');
*
* fs.readFile('myfile.txt', function(err, data){
* if (err) return handleError(err);
* handleData(data);
* });
* ```
*
* into:
*
* ```
* let fs = require('fs');
* let readFile = RSVP.denodeify(fs.readFile);
*
* readFile('myfile.txt').then(handleData, handleError);
* ```
*
* If the node function has multiple success parameters, then denodeify just
* returns the first one:
*
* ```
* let request = RSVP.denodeify(require('request'));
*
* request('http://example.com').then(function(res) {
* // ...
* });
* ```
*
* However, if you need all success parameters, setting denodeify's second
* parameter to true causes it to return all success parameters as an array:
*
* ```
* let request = RSVP.denodeify(require('request'), true);
*
* request('http://example.com').then(function(result) {
* // result[0] -> res
* // result[1] -> body
* });
* ```
*
* Or if you pass it an array with names it returns the parameters as a hash:
*
* ```
* let request = RSVP.denodeify(require('request'), ['res', 'body']);
*
* request('http://example.com').then(function(result) {
* // result.res
* // result.body
* });
* ```
*/
export function denodeify<A, T, C>(
nodeFunction: Function,
options: boolean | string[]
): (...args: A[]) => Promise<T, C>;
/**
* `RSVP.defer` returns an object similar to jQuery's `$.Deferred`.
* `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s
* interface. New code should use the `RSVP.Promise` constructor instead.
*
* The object returned from `RSVP.defer` is a plain object with three properties:
* * promise - an `RSVP.Promise`.
* * reject - a function that causes the `promise` property on this object to become rejected
* * resolve - a function that causes the `promise` property on this object to become fulfilled.
*/
export function defer<T, C>(label?: string): Deferred<T, C>;
/**
* `RSVP.Promise.reject` returns a promise rejected with the passed `reason`.
*/
export function reject<C>(reason: C): Promise<never, C>;
/**
* `RSVP.Promise.resolve` returns a promise that will become resolved with the
* passed `value`.
*/
export function resolve<T>(value: T): Promise<T, never>;
/**
* `RSVP.filter` is similar to JavaScript's native `filter` method, except that it
* waits for all promises to become fulfilled before running the `filterFn` on
* each item in given to `promises`. `RSVP.filter` returns a promise that will
* become fulfilled with the result of running `filterFn` on the values the
* promises become fulfilled with.
*/
export function filter<T, C>(
promises: Thenable<T, C>[],
filterFn: (value: T) => boolean | Promise<any, any>
): Promise<T[], C>;
/**
* `RSVP.rethrow` will rethrow an error on the next turn of the JavaScript event
* loop in order to aid debugging.
*
* Promises A+ specifies that any exceptions that occur with a promise must be
* caught by the promises implementation and bubbled to the last handler. For
* this reason, it is recommended that you always specify a second rejection
* handler function to `then`. However, `RSVP.rethrow` will throw the exception
* outside of the promise, so it bubbles up to your console if in the browser,
* or domain/cause uncaught exception in Node. `rethrow` will also throw the
* error again so the error can be handled by the promise per the spec.
*/
export function rethrow<C>(reason: C): void;
export const asap: typeof RSVP.asap;
export const cast: typeof RSVP.cast;
export const Promise: typeof RSVP.Promise;
export const EventTarget: typeof RSVP.EventTarget;
export const all: typeof RSVP.all;
export const allSettled: typeof RSVP.allSettled;
export const race: typeof RSVP.race;
export const hash: typeof RSVP.hash;
export const hashSettled: typeof RSVP.hashSettled;
export const rethrow: typeof RSVP.rethrow;
export const defer: typeof RSVP.defer;
export const denodeify: typeof RSVP.denodeify;
export const configure: typeof RSVP.configure;
export const on: typeof RSVP.on;
export const off: typeof RSVP.off;
export const resolve: typeof RSVP.resolve;
export const reject: typeof RSVP.reject;
export const map: typeof RSVP.map;
export const async: typeof RSVP.async;
export const filter: typeof RSVP.filter;
}
export = RSVP;

397
types/rsvp/rsvp-tests.ts Normal file → Executable file
View File

@ -1,83 +1,326 @@
import RSVP = require('rsvp');
import RSVP from 'rsvp';
import { all, race, resolve } from 'rsvp';
let promise1: RSVP.Promise<number, Error> = RSVP.Promise.resolve(1);
let promise1a: RSVP.Promise<number, Error> = RSVP.resolve(1);
/** Static assertion that `value` has type `T` */
// Disable tslint here b/c the generic is used to let us do a type coercion and
// validate that coercion works for the type value "passed into" the function.
// tslint:disable-next-line:no-unnecessary-generics
declare function assertType<T>(value: T): void;
let promise2: RSVP.Promise<number, Error> = RSVP.Promise.resolve(2);
async function testAsyncAwait() {
const awaitedNothing = await RSVP.resolve();
const awaitedValue = await RSVP.resolve('just a value');
let promise3: RSVP.Promise<number, Error> = RSVP.Promise.reject(new Error('3'));
let promise3a: RSVP.Promise<number, Error> = RSVP.reject(new Error('3'));
async function returnsAPromise(): RSVP.Promise<string> {
return RSVP.resolve('look, a string');
}
let promiseArray = [promise1, promise2, promise3];
let promiseHash = {
promiseA: promise1,
promiseB: promise2,
promiseC: promise3,
notAPromise: 4,
};
RSVP.Promise.all(promiseArray).then(arr => {}, err => {});
RSVP.all(promiseArray).then(arr => {}, err => {});
RSVP.Promise.race(promiseArray).then(arr => {}, err => {});
RSVP.race(promiseArray).then(arr => {}, err => {});
RSVP.allSettled(promiseArray).then(arr => {}, err => {});
let deferred = RSVP.defer();
deferred.resolve('Success');
deferred.promise.then(value => {});
let filterFn = (item: number) => {
return item > 1;
};
RSVP.filter(promiseArray, filterFn).then(result => {});
RSVP.hashSettled(promiseHash).then(hash => {
return (
hash.promiseA.state === 'fulfilled' &&
hash.promiseB.value === 2 &&
hash.promiseC.reason === '3' &&
hash.notAPromise.state === 'fulfilled'
);
});
RSVP.hash(promiseHash).then(
values => {
return (
values.promiseA < 0 &&
values.promiseB === 4 &&
values.promiseC === 12 &&
values.notAPromise > 0
);
},
err => {}
);
let mapFn = function(item: number) {
return item + 1;
};
RSVP.map(promiseArray, mapFn).then(function(result) {});
let promise = new Promise(function(resolve, reject) {
resolve();
reject();
});
promise.then(value => {}, reason => {});
function throws() {
throw new Error('Whoops!');
assertType<RSVP.Promise<string>>(returnsAPromise());
assertType<string>(await returnsAPromise());
}
let throwingPromise = new RSVP.Promise(function(resolve, reject) {
throws();
});
throwingPromise.catch(RSVP.rethrow).then(value => {}, reason => {});
let someObject = {};
RSVP.EventTarget.mixin(someObject);
RSVP.EventTarget.on('fulfilled', someString => {
return someString;
});
RSVP.EventTarget.trigger('fulfilled', 'woohoo');
RSVP.EventTarget.off('fulfilled');
function testCast() {
RSVP.Promise.cast('foo').then(value => {
assertType<string>(value);
});
RSVP.cast(42).then(value => {
assertType<number>(value);
});
}
function testConfigure() {
assertType<void>(RSVP.configure('name', { with: 'some value' }));
assertType<{}>(RSVP.configure('name'));
}
function testAsap() {
const result = RSVP.asap(something => {
console.log(something);
}, 'srsly');
assertType<void>(result);
}
function testAsync() {
const result = RSVP.async(something => {
console.log(something);
}, 'rly srsly');
assertType<void>(result);
}
function testPromise() {
const promiseOfString = new RSVP.Promise((resolve: any, reject: any) => resolve('some string'));
assertType<RSVP.Promise<number>>(promiseOfString.then((s: string) => s.length));
}
function testAll() {
const imported = all([]);
const empty = RSVP.Promise.all([]);
const everyPromise = RSVP.all([
'string',
RSVP.resolve(42),
RSVP.resolve({ hash: 'with values' }),
]);
assertType<RSVP.Promise<[string, number, { hash: string }]>>(everyPromise);
const anyFailure = RSVP.all([12, 'strings', RSVP.reject('anywhere')]);
assertType<RSVP.Promise<{}>>(anyFailure);
let promise1 = RSVP.resolve(1);
let promise2 = RSVP.resolve('2');
let promise3 = RSVP.resolve({ key: 13 });
RSVP.Promise.all([promise1, promise2, promise3], 'my label').then(function(array) {
assertType<number>(array[0]);
assertType<string>(array[1]);
assertType<{ key: number }>(array[2]);
});
}
function testAllSettled() {
const resolved1 = RSVP.resolve(1);
const resolved2 = RSVP.resolve('wat');
const rejected = RSVP.reject(new Error('oh teh noes'));
const pending = new RSVP.Promise<{ neato: string }>((resolve, reject) => {
if ('something') {
resolve({ neato: 'yay' });
} else {
reject('nay');
}
});
// Types flow into resolution properly
RSVP.allSettled([resolved1, resolved2, rejected, pending]).then(states => {
assertType<RSVP.PromiseState<number>>(states[0]);
assertType<RSVP.PromiseState<string>>(states[1]);
assertType<RSVP.PromiseState<never>>(states[2]);
assertType<RSVP.PromiseState<{ neato: string }>>(states[3]);
});
// Switching on state gives the correctly available items.
RSVP.allSettled([resolved1, resolved2, rejected, pending]).then(states => {
states.forEach(element => {
switch (element.state) {
case RSVP.State.fulfilled:
assertType<RSVP.Resolved<typeof element.value>>(element);
break;
case RSVP.State.rejected:
assertType<RSVP.Rejected<typeof element.reason>>(element);
break;
case RSVP.State.pending:
assertType<RSVP.Pending>(element);
break;
default:
// Someday maybe TS will have exhaustiveness checks.
break;
}
});
});
}
function testDefer() {
let deferred = RSVP.defer<string>();
deferred.resolve('Success!');
deferred.promise.then(function(value) {
assertType<string>(value);
});
}
// Using this to differentiate the types cleanly
type A1 = Array<{ arg: boolean }>;
type D1 = number;
type D2 = string;
type D3 = { some: boolean };
declare const nodeFn1Arg1CbParam: (arg1: A1, callback: (err: any, data: D1) => void) => void;
declare const nodeFn1Arg2CbParam: (
arg1: A1,
callback: (err: any, data1: D1, data2: D2) => void
) => void;
declare const nodeFn1Arg3CbParam: (
arg1: A1,
callback: (err: any, data1: D1, data2: D2, data3: D3) => void
) => void;
function testDenodeify() {
// version with no `options` or `options: false`, and single T
assertType<(value: A1) => RSVP.Promise<D1>>(RSVP.denodeify(nodeFn1Arg1CbParam));
assertType<(value: A1) => RSVP.Promise<D1>>(RSVP.denodeify(nodeFn1Arg1CbParam, false));
// version with no `options` or `options: false`, and multiple T
assertType<(value: A1) => RSVP.Promise<D1>>(RSVP.denodeify(nodeFn1Arg2CbParam));
assertType<(value: A1) => RSVP.Promise<D1>>(RSVP.denodeify(nodeFn1Arg3CbParam));
assertType<(value: A1) => RSVP.Promise<D1>>(RSVP.denodeify(nodeFn1Arg2CbParam, false));
assertType<(value: A1) => RSVP.Promise<D1>>(RSVP.denodeify(nodeFn1Arg3CbParam, false));
// version with `options: true` and single or multiple T
assertType<(value: A1) => RSVP.Promise<[D1]>>(RSVP.denodeify(nodeFn1Arg1CbParam, true));
assertType<(value: A1) => RSVP.Promise<[D1, D2]>>(RSVP.denodeify(nodeFn1Arg2CbParam, true));
assertType<(value: A1) => RSVP.Promise<[D1, D2, D3]>>(RSVP.denodeify(nodeFn1Arg3CbParam, true));
// We can't actually map the key names here, because we would need full-on
// dependent typing to use the *values of an array* as the keys of the
// resulting object.
assertType<(value: A1) => RSVP.Promise<{ first: D1 }>>(
RSVP.denodeify(nodeFn1Arg1CbParam, ['first'])
);
assertType<(value: A1) => RSVP.Promise<{ first: D1; second: D2 }>>(
RSVP.denodeify(nodeFn1Arg2CbParam, ['first', 'second'])
);
assertType<(value: A1) => RSVP.Promise<{ first: D1; second: D2; third: D3 }>>(
RSVP.denodeify(nodeFn1Arg3CbParam, ['first', 'second', 'third'])
);
const foo = RSVP.denodeify(nodeFn1Arg2CbParam, ['quux', 'baz']);
foo([{ arg: true }]).then(value => {
console.log(value.quux + 1);
console.log(value.baz.length);
});
}
function testFilter() {
RSVP.filter([RSVP.resolve(1), RSVP.resolve(2)], item => item > 1, 'over one').then(results => {
assertType<number[]>(results);
});
RSVP.filter(
[RSVP.resolve('a string'), RSVP.resolve(112233)],
item => String(item).length < 10,
'short string'
).then(results => {
assertType<Array<string | number>>(results);
});
// This is the best we can do: we can't actually write the full type here,
// which would be `assertType<never>(results)`, but TS can't infer that.
const isString = (item: any): item is string => typeof item === 'string';
RSVP.filter([RSVP.reject('for any reason')], isString).then(results => {
assertType<{}>(results);
});
}
function testHash() {
let promises = {
myPromise: RSVP.resolve(1),
yourPromise: RSVP.resolve('2'),
theirPromise: RSVP.resolve({ key: 3 }),
notAPromise: 4,
};
RSVP.hash(promises, 'my label').then(function(hash) {
assertType<number>(hash.myPromise);
assertType<string>(hash.yourPromise);
assertType<{ key: number }>(hash.theirPromise);
assertType<number>(hash.notAPromise);
});
}
function testHashSettled() {
function isFulfilled<T>(state: RSVP.PromiseState<T>): state is RSVP.Resolved<T> {
return state.state === RSVP.State.fulfilled;
}
let promises = {
myPromise: RSVP.Promise.resolve(1),
yourPromise: RSVP.Promise.resolve('2'),
theirPromise: RSVP.Promise.resolve({ key: 3 }),
notAPromise: 4,
};
RSVP.hashSettled(promises).then(function(hash) {
if (isFulfilled(hash.myPromise)) {
assertType<number>(hash.myPromise.value);
}
if (isFulfilled(hash.yourPromise)) {
assertType<string>(hash.yourPromise.value);
}
if (isFulfilled(hash.theirPromise)) {
assertType<{ key: number }>(hash.theirPromise.value);
}
if (isFulfilled(hash.notAPromise)) {
assertType<number>(hash.notAPromise.value);
}
});
}
function testMap() {
RSVP.map([RSVP.resolve(1), RSVP.resolve(2)], item => item + 1, 'add one').then(results => {
assertType<number[]>(results);
assertType<{ length: 2 }>(results);
});
RSVP.map([RSVP.resolve('a string'), RSVP.resolve(112233)], String).then(results => {
assertType<string[]>(results);
assertType<{ length: 2 }>(results);
});
// This is the best we can do: we can't actually write the full type here,
// which would be `assertType<never>(results)`, but TS can't infer that.
RSVP.map([RSVP.reject('for any reason')], String).then(results => {
assertType<{}>(results);
});
}
function testRace() {
const imported = race([]);
const firstPromise = RSVP.race([{ notAPromise: true }, RSVP.resolve({ some: 'value' })]);
assertType<RSVP.Promise<{ notAPromise: boolean } | { some: string }>>(firstPromise);
let promise1 = RSVP.resolve(1);
let promise2 = RSVP.resolve('2');
RSVP.Promise.race([promise1, promise2], 'my label').then(function(result) {
assertType<string | number>(result);
});
}
function testReject() {
assertType<RSVP.Promise<never>>(RSVP.reject());
assertType<RSVP.Promise<never>>(RSVP.reject('this is a string'));
RSVP.reject({ ok: false }).catch(reason => {
console.log(`${reason} could be anything`);
});
RSVP.reject({ ok: false }, 'some label').catch((reason: any) => reason.ok);
let promise = RSVP.Promise.reject(new Error('WHOOPS'));
}
function testResolve() {
assertType<RSVP.Promise<void>>(RSVP.resolve());
assertType<RSVP.Promise<string>>(RSVP.resolve('this is a string'));
assertType<RSVP.Promise<string>>(RSVP.resolve(RSVP.resolve('nested')));
assertType<RSVP.Promise<string>>(RSVP.resolve(Promise.resolve('nested')));
let promise = RSVP.Promise.resolve(1);
let imported = resolve(1);
}
function testRethrow() {
RSVP.reject(new Error('all the badness'))
.catch(RSVP.rethrow)
.then(value => {
assertType<void>(value);
})
.catch(reason => {
if (reason instanceof Error) {
console.log(reason);
}
});
}
function testOnAndOff() {
RSVP.on('error', (reason: Error) => {
console.log(`it was an error: ${reason}`);
});
RSVP.off('whatever', (value: any) => {
console.log(
`any old value will do: ${value !== undefined && value !== null
? value.toString()
: 'even undefined'}`
);
});
}

18
types/rsvp/tsconfig.json Normal file → Executable file
View File

@ -1,23 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"target": "es5",
"lib": ["es6", "dom"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictFunctionTypes": false,
"baseUrl": "../",
"typeRoots": [
"../"
],
"typeRoots": ["../"],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"rsvp-tests.ts"
]
}
"files": ["index.d.ts", "rsvp-tests.ts"]
}