[ember] new major release for types

This commit is contained in:
Mike North
2018-09-02 12:21:01 -07:00
parent 6ef608b70c
commit b1e8f28d7c
35 changed files with 5701 additions and 1 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
// Type definitions for Ember.js 2.8
// Type definitions for Ember.js 3.0
// Project: http://emberjs.com/
// Definitions by: Jed Mao <https://github.com/jedmao>
// bttf <https://github.com/bttf>
Vendored Executable
+3898
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
import ApplicationInstance from '@ember/application/instance';
import hbs from 'htmlbars-inline-precompile';
const appInstance = ApplicationInstance.create();
appInstance.register('some:injection', class Foo {});
appInstance.register('some:injection', class Foo {}, {
singleton: true,
});
appInstance.register('some:injection', class Foo {}, {
instantiate: false,
});
appInstance.register('templates:foo/bar', hbs`<h1>Hello World</h1>`);
appInstance.register('some:injection', class Foo {}, {
singleton: false,
instantiate: true,
});
appInstance.factoryFor('router:main');
appInstance.lookup('route:basic');
appInstance.boot();
(async function() {
await appInstance.boot();
}());
+35
View File
@@ -0,0 +1,35 @@
import Ember from 'ember';
import { assertType } from "./lib/assert";
let BaseApp = Ember.Application.extend({
modulePrefix: 'my-app'
});
BaseApp.initializer({
name: 'my-initializer',
initialize(app) {
app.register('foo:bar', Ember.Object.extend({ foo: 'bar' }));
}
});
BaseApp.instanceInitializer({
name: 'my-instance-initializer',
initialize(app) {
app.lookup('foo:bar').get('foo');
}
});
let App1 = BaseApp.create({
rootElement: '#app-one',
customEvents: {
paste: 'paste'
}
});
let App2 = BaseApp.create({
rootElement: '#app-two',
customEvents: {
mouseenter: null,
mouseleave: null
}
});
+21
View File
@@ -0,0 +1,21 @@
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')));
assertType<Person[]>(array.sortBy('name'));
assertType<Person[]>(array.uniq());
assertType<Person[]>(array.uniqBy('name'));
+26
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.create({ content: Ember.A([1, 2, 3]) }) as MyNewProxy<number>;
assertType<number | undefined>(x.get('firstObject'));
assertType<boolean>(x.isNew);
+48
View File
@@ -0,0 +1,48 @@
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<Ember.Enumerable<Person>>(people.filterBy('isHappy'));
assertType<Ember.Enumerable<Person>>(people.rejectBy('isHappy'));
assertType<Ember.Enumerable<Person>>(people.filter((person) => person.get('name') === 'Yehuda'));
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
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
},
});
+166
View File
@@ -0,0 +1,166 @@
import Ember from 'ember';
import Component from '@ember/component';
import Computed, { alias, 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(),
explicitlyDeclared: alias('fullName') as Computed<string>,
});
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<Ember.ComputedProperty<string>>(person.explicitlyDeclared);
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<string>(person.get('explicitlyDeclared'));
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')
}).create();
assertType<boolean>(component2.get('isAnimal'));
+11
View File
@@ -0,0 +1,11 @@
import Controller from '@ember/controller';
Controller.extend ({
queryParams: ['category'],
category: null,
isExpanded: false,
toggleBody() {
this.toggleProperty('isExpanded');
}
});
+7
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);
+20
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
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);
}
+170
View File
@@ -0,0 +1,170 @@
import Ember from 'ember';
let App: any;
App = Ember.Application.create();
App.president = Ember.Object.create({
name: 'Barack Obama',
});
App.country = Ember.Object.create({
presidentNameBinding: 'MyApp.president.name',
});
App.country.get('presidentName');
App.president = Ember.Object.create({
firstName: 'Barack',
lastName: 'Obama',
fullName: Ember.computed(function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
}),
});
App.president.get('fullName');
declare class MyPerson extends Ember.Object {
static createMan(): MyPerson;
}
MyPerson.createMan();
const Person1 = Ember.Object.extend({
say: (thing: string) => {
alert(thing);
},
});
declare class MyPerson2 extends Ember.Object {
helloWorld(): void;
}
MyPerson2.create().helloWorld();
const tom = Person1.create({
name: 'Tom Dale',
helloWorld() {
this.say('Hi my name is ' + this.get('name'));
},
});
tom.helloWorld();
const PersonReopened = Person1.reopen({ isPerson: true });
PersonReopened.create().get('isPerson');
App.todosController = Ember.Object.create({
todos: [Ember.Object.create({ isDone: false })],
remaining: Ember.computed('todos.@each.isDone', function() {
const todos = this.get('todos');
return todos.filterProperty('isDone', false).get('length');
}),
});
const todos = App.todosController.get('todos');
let todo = todos.objectAt(0);
todo.set('isDone', true);
App.todosController.get('remaining');
todo = Ember.Object.create({ isDone: false });
todos.pushObject(todo);
App.todosController.get('remaining');
App.wife = Ember.Object.create({
householdIncome: 80000,
});
App.husband = Ember.Object.create({
householdIncomeBinding: 'App.wife.householdIncome',
});
App.husband.get('householdIncome');
App.husband.set('householdIncome', 90000);
App.wife.get('householdIncome');
App.user = Ember.Object.create({
fullName: 'Kara Gates',
});
App.user.set('fullName', 'Krang Gates');
App.userView.set('userName', 'Truckasaurus Gates');
App.user.get('fullName');
App = Ember.Application.create({
rootElement: '#sidebar',
});
App.userController = Ember.Object.create({
content: Ember.Object.create({
firstName: 'Albert',
lastName: 'Hofmann',
posts: 25,
hobbies: 'Riding bicycles',
}),
});
Handlebars.registerHelper(
'highlight',
(property: string, options: any) =>
new Handlebars.SafeString('<span class="highlight">' + 'some value' + '</span>')
);
const coolView = App.CoolView.create();
const Person2 = Ember.Object.extend({
name: '',
sayHello() {
console.log('Hello from ' + this.get('name'));
},
});
const people = Ember.A([
Person2.create({ name: 'Juan' }),
Person2.create({ name: 'Charles' }),
Person2.create({ name: 'Majd' }),
]);
people.invoke('sayHello');
const arr = Ember.A([Ember.Object.create(), Ember.Object.create()]);
arr.setEach('name', 'unknown');
arr.getEach('name');
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: typeof Person3.prototype): boolean => {
return !!person.get('isHappy');
};
people2.every(isHappy);
people2.any(isHappy);
people2.isEvery('isHappy');
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>((resolve: Function, reject: Function) => {
// on success
resolve('ok!');
// on failure
reject('no-k!');
});
promise.then(
(value: any) => {
// on fulfillment
},
(reason: any) => {
// on rejection
}
);
// 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,
});
const mix2 = Ember.Mixin.create({
bar: 2,
});
const component1 = Ember.Component.extend(mix1, mix2, {
lyft: Ember.inject.service(),
cars: Ember.computed.readOnly('lyft.cars'),
});
+26
View File
@@ -0,0 +1,26 @@
import EngineInstance from '@ember/engine/instance';
const engineInstance = EngineInstance.create();
engineInstance.register('some:injection', class Foo {});
engineInstance.register('some:injection', class Foo {}, {
singleton: true,
});
engineInstance.register('some:injection', class Foo {}, {
instantiate: false,
});
engineInstance.register('some:injection', class Foo {}, {
singleton: false,
instantiate: true,
});
engineInstance.factoryFor('router:main');
engineInstance.lookup('route:basic');
engineInstance.boot();
(async function() {
await engineInstance.boot();
}());
+6
View File
@@ -0,0 +1,6 @@
import { assertType } from "./lib/assert";
import Ember from "ember";
import EmberError from "@ember/error";
assertType<typeof Ember.Error>(EmberError);
+58
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
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);
+21
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'),
});
+41
View File
@@ -0,0 +1,41 @@
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');
},
});
import { helper } from '@ember/component/helper';
function typedHelp(/*params, hash*/) {
return 'my type of help';
}
export default helper(typedHelp);
function arrayNumHelp(/*params, hash*/) {
return [1, 2, 3];
}
helper(arrayNumHelp);
+59
View File
@@ -0,0 +1,59 @@
import Ember from 'ember';
class AuthService extends Ember.Service {
isAuthenticated: boolean;
}
class ApplicationController extends Ember.Controller {
model: {};
string: string;
transitionToLogin() {}
}
declare module '@ember/service' {
interface Registry {
'auth': AuthService;
}
}
declare module '@ember/controller' {
interface Registry {
'application': ApplicationController;
}
}
class LoginRoute extends Ember.Route {
auth = Ember.inject.service('auth');
application = Ember.inject.controller('application');
didTransition() {
if (!this.get('auth').get('isAuthenticated')) {
this.get('application').transitionToLogin();
}
}
anyOldMethod() {
this.controllerFor('application').set('string', 'must be a string');
}
}
// New module injection style.
import Controller, { inject as controller } from '@ember/controller';
import Service, { inject as service } from '@ember/service';
import { assertType } from './lib/assert';
class ComponentInjection extends Ember.Component {
applicationController = controller('application');
auth = service('auth');
router = service('router');
misc = service();
testem() {
assertType<Ember.Service>(this.get('misc'));
const url = this.get('router').urlFor('some-route', 1, 2, 3, { queryParams: { seriously: 'yes' } });
assertType<string>(url);
if (!this.get('auth').isAuthenticated) {
this.get('applicationController').transitionToLogin();
}
}
}
+5
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;
+57
View File
@@ -0,0 +1,57 @@
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);
/* Test composition of mixins */
const EditableAndCancelableMixin = Ember.Mixin.create(EditableMixin, {
cancelled: false,
});
const EditableAndCancelableComment = Ember.Route.extend(EditableAndCancelableMixin);
const editableAndCancelable = EditableAndCancelableComment.create();
assertType<boolean>(editableAndCancelable.isEditing);
assertType<boolean>(editableAndCancelable.cancelled);
+27
View File
@@ -0,0 +1,27 @@
import Ember from 'ember';
const LifetimeHooks = Ember.Object.extend({
resource: null as {} | null,
init() {
this._super();
this.resource = {};
},
willDestroy() {
delete this.resource;
this._super();
}
});
class MyObject30 extends Ember.Object {
constructor() {
super();
}
}
class MyObject31 extends Ember.Object {
constructor(properties: object) {
super(properties);
}
}
+113
View File
@@ -0,0 +1,113 @@
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);
const lambda = () => {
this.fooDidChange(this, 'foo');
};
this.addObserver('foo', lambda);
this.removeObserver('foo', lambda);
Ember.addObserver(this, 'foo', lambda);
Ember.removeObserver(this, 'foo', lambda);
}
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 }));
}
function testDynamic() {
const obj: any = {};
const dynamicKey: string = 'dummy'; // tslint:disable-line:no-inferrable-types
assertType<any>(Ember.get(obj, 'dummy'));
assertType<any>(Ember.get(obj, dynamicKey));
assertType<string>(Ember.getWithDefault(obj, 'dummy', 'default'));
assertType<string>(Ember.getWithDefault(obj, dynamicKey, 'default'));
assertType<{ dummy: any }>(Ember.getProperties(obj, 'dummy'));
assertType<{ dummy: any }>(Ember.getProperties(obj, [ 'dummy' ]));
assertType<object>(Ember.getProperties(obj, dynamicKey));
assertType<object>(Ember.getProperties(obj, [ dynamicKey ]));
assertType<string>(Ember.set(obj, 'dummy', 'value'));
assertType<string>(Ember.set(obj, dynamicKey, 'value'));
assertType<{ dummy: string }>(Ember.setProperties(obj, { dummy: 'value '}));
assertType<object>(Ember.setProperties(obj, { [dynamicKey]: 'value' }));
}
+65
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);
+104
View File
@@ -0,0 +1,104 @@
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);
},
});
class RouteUsingClass extends Route.extend({
randomProperty: 'the .extend + extends bit type-checks properly',
}) {
beforeModel(this: RouteUsingClass) {
return 'beforeModel can return anything, not just promises';
}
intermediateTransitionWithoutModel() {
this.intermediateTransitionTo('some-route');
}
intermediateTransitionWithModel() {
this.intermediateTransitionTo('some.other.route', { });
}
intermediateTransitionWithMultiModel() {
this.intermediateTransitionTo('some.other.route', 1, 2, { });
}
}
+54
View File
@@ -0,0 +1,54 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
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');
this.mount('my-engine', { as: 'some-other-engine', path: '/some-other-engine'});
});
const RouterServiceConsumer = Ember.Service.extend({
router: Ember.inject.service('router'),
currentRouteName() {
const x: string = Ember.get(this, 'router').currentRouteName;
},
currentURL() {
const x: string = Ember.get(this, 'router').currentURL;
},
transitionWithoutModel() {
Ember.get(this, 'router')
.transitionTo('some-route');
},
transitionWithModel() {
const model = Ember.Object.create();
Ember.get(this, 'router')
.transitionTo('some.other.route', model);
},
transitionWithMultiModel() {
const model = Ember.Object.create();
Ember.get(this, 'router')
.transitionTo('some.other.route', model, model);
},
transitionWithModelAndOptions() {
const model = Ember.Object.create();
Ember.get(this, 'router')
.transitionTo('index', model, { queryParams: { search: 'ember' }});
}
});
+204
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
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
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
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}`;
}));
}
+5
View File
@@ -0,0 +1,5 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const { ViewUtils: { isSimpleClick } } = Ember;
assertType<boolean>(isSimpleClick(new Event('wat')));
+58
View File
@@ -0,0 +1,58 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"ember": ["ember/v2"]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"test/lib/assert.ts",
"test/application.ts",
"test/application-instance.ts",
"test/engine-instance.ts",
"test/ember-tests.ts",
"test/error.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",
"test/view-utils.ts"
]
}
+30
View File
@@ -0,0 +1,30 @@
{
"extends": "dtslint/dt.json",
"rules": {
// Heavy use of Function type in this older package.
"ban-types": false,
"jsdoc-format": false,
"no-misused-new": false,
// these are disabled because of rfc176 module exports
"strict-export-declare-modifiers": false,
"no-single-declare-module": 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,
// false positives
"unified-signatures": false
}
}