Merge pull request #28282 from mike-north/new-cp-mapping

[@types/ember] rewrite computed property unwrapping to use infer
This commit is contained in:
Mine Starks
2018-09-05 08:25:21 -07:00
committed by GitHub
103 changed files with 9585 additions and 89 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
// Type definitions for ember-data 2.14
// Type definitions for ember-data 3.0
// Project: https://github.com/emberjs/data
// Definitions by: Derek Wickern <https://github.com/dwickern>
// Mike North <https://github.com/mike-north>
// Chris Krycho <https://github.com/chriskrycho>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
declare module 'ember-data' {
import Ember from 'ember';
+4
View File
@@ -0,0 +1,4 @@
import { DS } from "ember-data";
export default DS.Adapter;
export { AdapterRegistry } from 'ember-data';
+13
View File
@@ -0,0 +1,13 @@
import DS from 'ember-data';
declare const AdapterError: typeof DS.AdapterError;
declare const InvalidError: typeof DS.InvalidError;
declare const UnauthorizedError: typeof DS.UnauthorizedError;
declare const ForbiddenError: typeof DS.ForbiddenError;
declare const NotFoundError: typeof DS.NotFoundError;
declare const ConflictError: typeof DS.ConflictError;
declare const ServerError: typeof DS.ServerError;
declare const TimeoutError: typeof DS.TimeoutError;
declare const AbortError: typeof DS.AbortError;
declare const errorsHashToArray: typeof DS.errorsHashToArray;
declare const errorsArrayToHash: typeof DS.errorsArrayToHash;
+2
View File
@@ -0,0 +1,2 @@
import DS from 'ember-data';
export default DS.JSONAPIAdapter;
+2
View File
@@ -0,0 +1,2 @@
import DS from 'ember-data';
export default DS.RESTAdapter;
+2
View File
@@ -0,0 +1,2 @@
import DS from 'ember-data';
export default DS.attr;
+2121
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
import { DS } from 'ember-data';
export default DS.Model;
export { ModelRegistry } from 'ember-data';
+4
View File
@@ -0,0 +1,4 @@
import DS from 'ember-data';
declare const hasMany: typeof DS.hasMany;
declare const belongsTo: typeof DS.belongsTo;
+4
View File
@@ -0,0 +1,4 @@
import DS from 'ember-data';
export default DS.Serializer;
export { SerializerRegistry } from 'ember-data';
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.EmbeddedRecordsMixin;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.JSONAPISerializer;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.JSONSerializer;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.RESTSerializer;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.Store;
+120
View File
@@ -0,0 +1,120 @@
import Ember from 'ember';
import DS from 'ember-data';
class Session extends Ember.Service {}
declare module '@ember/service' {
interface Registry { 'session': Session; }
}
const JsonApi = DS.JSONAPIAdapter.extend({
// Application specific overrides go here
});
const Customized = DS.JSONAPIAdapter.extend({
host: 'https://api.example.com',
namespace: 'api/v1',
headers: {
'API_KEY': 'secret key',
'ANOTHER_HEADER': 'Some header value'
}
});
const AuthTokenHeader = DS.JSONAPIAdapter.extend({
session: Ember.inject.service('session'),
headers: Ember.computed('session.authToken', function() {
return {
'API_KEY': this.get('session.authToken'),
'ANOTHER_HEADER': 'Some header value'
};
})
});
const UseAjax = DS.JSONAPIAdapter.extend({
query(store: DS.Store, type: string, query: object) {
const url = 'https://api.example.com/my-api';
return this.ajax(url, 'POST', {
param: 'foo'
});
}
});
const UseAjaxOptions = DS.JSONAPIAdapter.extend({
query(store: DS.Store, type: string, query: object) {
const url = 'https://api.example.com/my-api';
const options = this.ajaxOptions(url, 'DELETE', {
foo: 'bar'
});
return Ember.$.ajax(url, {
...options
});
}
});
const UseAjaxOptionsWithOptionalThirdParams = DS.JSONAPIAdapter.extend({
query(store: DS.Store, type: string, query: object) {
const url = 'https://api.example.com/my-api';
const options = this.ajaxOptions(url, 'DELETE');
return Ember.$.ajax(url, {
...options
});
}
});
declare module 'ember-data' {
interface ModelRegistry {
'rootModel': any;
'super-user': any;
}
}
// https://github.com/emberjs/data/blob/c9d8212c857ca78218ad98d11621819b38dba98f/tests/unit/adapters/build-url-mixin/build-url-test.js
const BuildURLAdapter = DS.RESTAdapter.extend({
worksWithOnlyModelNameAndId() {
this.buildURL('rootModel', 1);
},
worksWithFindRecord() {
this.buildURL('super-user', 1, {} as any, 'findRecord');
},
worksWithFindAll() {
this.buildURL('super-user', null, {} as any, 'findAll');
},
worksWithQueryStub() {
this.buildURL('super-user', null, null, 'query', { limit: 10 });
},
worksWithQueryRecord() {
this.buildURL('super-user', null, null, 'queryRecord', { companyId: 10 });
},
worksWithFindMany() {
this.buildURL('super-user', [1, 2, 3], null, 'findMany');
},
worksWithFindHasMany() {
this.buildURL('super-user', 1, {} as any, 'findHasMany');
},
worksWithFindBelongsTo() {
this.buildURL('super-user', 1, {} as any, 'findBelongsTo');
},
worksWithCreateRecord() {
this.buildURL('super-user', 1, {} as any, 'createRecord');
},
worksWithUpdateRecord() {
this.buildURL('super-user', 1, {} as any, 'updateRecord');
},
worksWithDeleteRecord() {
this.buildURL('super-user', 1, {} as any, 'deleteRecord');
},
worksWithUnknownRequestType() {
this.buildURL('super-user', 1, null, 'unknown');
this.buildURL('super-user', null, null, 'unknown');
}
});
+29
View File
@@ -0,0 +1,29 @@
import DS from 'ember-data';
import { assertType } from './lib/assert';
declare const store: DS.Store;
class Folder extends DS.Model {
name = DS.attr('string');
children = DS.hasMany('folder', { inverse: 'parent' });
parent = DS.belongsTo('folder', { inverse: 'children' });
}
declare module 'ember-data' {
interface ModelRegistry {
folder: Folder;
}
}
const folder = Folder.create();
assertType<Folder>(folder.get('parent'));
assertType<string>(folder.get('parent').get('name'));
folder.get('parent').then(parent => {
assertType<Folder>(parent);
assertType<string>(parent.get('name'));
folder.set('parent', parent);
});
folder.set('parent', folder);
folder.set('parent', folder.get('parent'));
folder.set('parent', store.findRecord('folder', 3));
+106
View File
@@ -0,0 +1,106 @@
import Ember from 'ember';
import DS from 'ember-data';
import { assertType } from './lib/assert';
const { AdapterError } = DS;
// https://emberjs.com/api/ember-data/2.16/classes/DS.AdapterError
const MaintenanceError = DS.AdapterError.extend({
message: 'Down for maintenance.',
});
const maintenanceError = new MaintenanceError();
assertType<DS.AdapterError>(maintenanceError);
// https://emberjs.com/api/ember-data/2.16/classes/DS.InvalidError
const anInvalidError = new DS.InvalidError([
{
detail: 'Must be unique',
source: { pointer: '/data/attributes/title' },
},
{
detail: 'Must not be blank',
source: { pointer: '/data/attributes/content' },
},
]);
// https://emberjs.com/api/ember-data/2.16/classes/DS.TimeoutError
const { TimeoutError } = DS;
const timedOut = Ember.Route.extend({
actions: {
error(error: any, transition: any) {
if (error instanceof TimeoutError) {
// alert the user
alert('Are you still connected to the internet?');
return;
}
// ...other error handling logic
},
},
});
// This is technically private, but publicly exposed for APIs to use. We just
// check that it is a proper subclass of `AdapterError`.
// https://emberjs.com/api/ember-data/2.16/classes/DS.AbortError
// https://github.com/emberjs/data/blob/v2.16.0/addon/-private/adapters/errors.js#L206-L216
const { AbortError } = DS;
assertType<typeof AdapterError>(AbortError);
// https://emberjs.com/api/ember-data/2.16/classes/DS.UnauthorizedError
const { UnauthorizedError } = DS;
assertType<typeof AdapterError>(UnauthorizedError);
const unauthorized = Ember.Route.extend({
actions: {
error(error: any, transition: any) {
if (error instanceof UnauthorizedError) {
// go to the sign in route
this.transitionTo('login');
return;
}
// ...other error handling logic
},
},
});
// This is technically private, but publicly exposed for APIs to use. We just
// check that it is a proper subclass of `AdapterError`.
// https://emberjs.com/api/ember-data/2.16/classes/DS.ForbiddenError
// https://github.com/emberjs/data/blob/v2.16.0/addon/-private/adapters/errors.js#L253-L263
const { ForbiddenError } = DS;
assertType<typeof AdapterError>(ForbiddenError);
// https://emberjs.com/api/ember-data/2.16/classes/DS.NotFoundError
const { NotFoundError } = DS;
assertType<typeof AdapterError>(NotFoundError);
const notFound = Ember.Route.extend({
model(params: { post_id: string }): any {
return this.get('store').findRecord('post', params.post_id);
},
actions: {
error(error: any, transition: any): any {
if (error instanceof NotFoundError) {
// redirect to a list of all posts instead
this.transitionTo('posts');
} else {
// otherwise let the error bubble
return true;
}
},
},
});
// This is technically private, but publicly exposed for APIs to use. We just
// check that it is a proper subclass of `AdapterError`.
// https://emberjs.com/api/ember-data/2.16/classes/DS.ConflictError
// https://github.com/emberjs/data/blob/v2.16.0/addon/-private/adapters/errors.js#L303-L313
const { ConflictError } = DS;
assertType<typeof AdapterError>(ConflictError);
// This is technically private, but publicly exposed for APIs to use. We just
// check that it is a proper subclass of `AdapterError`.
// https://emberjs.com/api/ember-data/2.16/classes/DS.ServerError
// https://github.com/emberjs/data/blob/v2.16.0/addon/-private/adapters/errors.js#L315-L323
const { ServerError } = DS;
assertType<typeof AdapterError>(ServerError);
+61
View File
@@ -0,0 +1,61 @@
import Ember from 'ember';
import DS from 'ember-data';
import { assertType } from './lib/assert';
class BlogComment extends DS.Model {
text = DS.attr('string');
}
declare module 'ember-data' {
interface ModelRegistry {
'blog-comment': BlogComment;
}
}
class BlogPost extends DS.Model {
title = DS.attr('string');
commentsAsync = DS.hasMany('blog-comment');
commentsSync = DS.hasMany('blog-comment', { async: false });
}
const blogPost = BlogPost.create();
assertType<DS.PromiseArray<BlogComment>>(blogPost.get('commentsSync').reload());
assertType<BlogComment>(blogPost.get('commentsSync').createRecord());
const comment = blogPost.get('commentsSync').get('firstObject');
assertType<BlogComment | undefined>(comment);
if (comment) {
assertType<string>(comment.get('text'));
}
assertType<DS.PromiseArray<BlogComment>>(blogPost.get('commentsAsync').reload());
assertType<BlogComment>(blogPost.get('commentsAsync').createRecord());
assertType<BlogComment | undefined>(blogPost.get('commentsAsync').get('firstObject'));
const commentAsync = blogPost.get('commentsAsync').get('firstObject');
assertType<BlogComment | undefined>(commentAsync);
if (commentAsync) {
assertType<string>(commentAsync.get('text'));
}
assertType<boolean>(blogPost.get('commentsAsync').get('isFulfilled'));
blogPost.get('commentsAsync').then(comments => {
assertType<BlogComment | undefined>(comments.get('firstObject'));
assertType<string>(comments.get('firstObject')!.get('text'));
});
blogPost.set('commentsAsync', blogPost.get('commentsAsync'));
blogPost.set('commentsAsync', Ember.A());
blogPost.set('commentsAsync', Ember.A([ comment! ]));
class PaymentMethod extends DS.Model {}
declare module 'ember-data' {
interface ModelRegistry {
'payment-method': PaymentMethod;
}
}
class Polymorphic extends DS.Model {
paymentMethods = DS.hasMany('payment-method', { polymorphic: true });
}
+30
View File
@@ -0,0 +1,30 @@
import Ember from 'ember';
import DS from 'ember-data';
class MyModel extends DS.Model {}
declare module 'ember-data' {
interface ModelRegistry {
'my-model': MyModel;
}
}
Ember.Route.extend({
model(): any {
return this.store.findAll('my-model');
}
});
Ember.Controller.extend({
actions: {
create(): any {
return this.store.createRecord('my-model');
}
}
});
Ember.DataAdapter.extend({
test() {
this.store.findRecord('my-model', 123);
}
});
+2
View File
@@ -0,0 +1,2 @@
/** Static assertion that `value` has type `T` */
export declare function assertType<T>(value: T): void;
+37
View File
@@ -0,0 +1,37 @@
import Ember from 'ember';
import DS, { ChangedAttributes } from 'ember-data';
import { assertType } from "./lib/assert";
const Person = DS.Model.extend({
firstName: DS.attr(),
lastName: DS.attr(),
title: DS.attr({ defaultValue: "The default" }),
title2: DS.attr({ defaultValue: () => "The default" }),
fullName: Ember.computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
})
});
const User = DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
verified: DS.attr('boolean', { defaultValue: false }),
canBeNull: DS.attr('boolean', { allowNull: true }),
createdAt: DS.attr('date', {
defaultValue() { return new Date(); }
})
});
const user = User.create({ username: 'dwickern' });
assertType<string>(user.get('id'));
assertType<string>(user.get('username'));
assertType<boolean>(user.get('verified'));
assertType<Date>(user.get('createdAt'));
user.serialize();
user.serialize({ includeId: true });
user.serialize({ includeId: true });
const attributes = user.changedAttributes();
assertType<ChangedAttributes>(attributes);
+91
View File
@@ -0,0 +1,91 @@
/**
* Tests for the Ember-Data "module API" introduced in v2.3
* @see https://www.emberjs.com/blog/2016/01/12/ember-data-2-3-released.html#toc_importing-modules
*/
import DS from 'ember-data';
// Adapters
import Adapter from 'ember-data/adapter';
import JSONAPIAdapter from 'ember-data/adapters/json-api';
import RESTAdapter from 'ember-data/adapters/rest';
// Serializers
import Serializer from 'ember-data/serializer';
import RESTSerializer from 'ember-data/serializers/rest';
import JSONSerializer from 'ember-data/serializers/json';
import JSONAPISerializer from 'ember-data/serializers/json-api';
// Model
import Model from 'ember-data/model';
// Model - attr
import attr from 'ember-data/attr';
// Model - relationships
import { hasMany, belongsTo } from 'ember-data/relationships';
// Transforms
import BooleanTransform from 'ember-data/transforms/boolean';
import StringTransform from 'ember-data/transforms/string';
import NumberTransform from 'ember-data/transforms/number';
import DateTransform from 'ember-data/transforms/date';
import Transform from 'ember-data/transforms/transform';
// Store
import Store from 'ember-data/store';
// Errors
import * as EDErrors from 'ember-data/adapters/errors';
import { assertType } from "./lib/assert";
// ADAPTERS
// - identity
assertType<typeof DS.Adapter>(Adapter);
assertType<typeof DS.RESTAdapter>(RESTAdapter);
assertType<typeof DS.JSONAPIAdapter>(JSONAPIAdapter);
// - inheritance
assertType<typeof DS.Adapter>(RESTAdapter);
assertType<typeof DS.RESTAdapter>(JSONAPIAdapter);
// SERIALIZERS
// - identity
assertType<typeof DS.Serializer>(Serializer);
assertType<typeof DS.RESTSerializer>(RESTSerializer);
assertType<typeof DS.JSONSerializer>(JSONSerializer);
assertType<typeof DS.JSONAPISerializer>(JSONAPISerializer);
// - inheritance
assertType<typeof DS.Serializer>(JSONSerializer);
assertType<typeof DS.JSONSerializer>(RESTSerializer);
assertType<typeof DS.JSONSerializer>(JSONAPISerializer);
// MODEL
// - identity
assertType<typeof DS.Model>(Model);
// - attributes
assertType<typeof DS.attr>(attr);
// - relationships
assertType<typeof DS.hasMany>(hasMany);
assertType<typeof DS.belongsTo>(belongsTo);
// TRANSFORMS
// - identity
assertType<typeof DS.BooleanTransform>(BooleanTransform);
assertType<typeof DS.NumberTransform>(NumberTransform);
assertType<typeof DS.StringTransform>(StringTransform);
assertType<typeof DS.DateTransform>(DateTransform);
assertType<typeof DS.Transform>(Transform);
// STORE
// - identity
assertType<typeof DS.Store>(Store);
// ERRORS
// - identity
assertType<typeof DS.AdapterError>(EDErrors.AdapterError);
assertType<typeof DS.InvalidError>(EDErrors.InvalidError);
assertType<typeof DS.UnauthorizedError>(EDErrors.UnauthorizedError);
assertType<typeof DS.ForbiddenError>(EDErrors.ForbiddenError);
assertType<typeof DS.NotFoundError>(EDErrors.NotFoundError);
assertType<typeof DS.ConflictError>(EDErrors.ConflictError);
assertType<typeof DS.ServerError>(EDErrors.ServerError);
assertType<typeof DS.TimeoutError>(EDErrors.TimeoutError);
assertType<typeof DS.AbortError>(EDErrors.AbortError);
assertType<typeof DS.errorsHashToArray>(EDErrors.errorsHashToArray);
assertType<typeof DS.errorsArrayToHash>(EDErrors.errorsArrayToHash);
@@ -0,0 +1,44 @@
import DS from 'ember-data';
import { assertType } from "./lib/assert";
declare const store: DS.Store;
class User extends DS.Model {
username = DS.attr('string');
}
declare module 'ember-data' {
interface ModelRegistry {
user: User;
}
}
let userRef = store.getReference('user', 1);
// get the record of the reference (null if not yet available)
let user = userRef.value();
if (user !== null) {
assertType<User>(user);
}
// get the identifier of the reference
if (userRef.remoteType() === 'id') {
let id = userRef.id();
assertType<string>(id);
}
// load user (via store.find)
userRef.load().then(user => {
assertType<User>(user);
});
// or trigger a reload
userRef.reload().then(user => {
assertType<User>(user);
});
// provide data for reference
userRef.push({ id: 1, username: '@user' }).then(function(user) {
assertType<User>(user);
userRef.value() === user;
});
+60
View File
@@ -0,0 +1,60 @@
import Ember from 'ember';
import DS from 'ember-data';
declare const store: DS.Store;
const Person = DS.Model.extend({
children: DS.hasMany('folder', { inverse: 'parent' }),
parent: DS.belongsTo('folder', { inverse: 'children' })
});
const Polymorphic = DS.Model.extend({
paymentMethods: DS.hasMany('payment-method', { polymorphic: true })
});
Polymorphic.eachRelationship(() => '');
Polymorphic.eachRelationship(() => '', {});
Polymorphic.eachRelationship((n, meta) => {
let s: string = n;
let m: 'belongsTo' | 'hasMany' = meta.kind;
});
let p = Polymorphic.create();
p.eachRelationship(() => '');
p.eachRelationship(() => '', {});
p.eachRelationship((n, meta) => {
let s: string = n;
let m: 'belongsTo' | 'hasMany' = meta.kind;
});
class Comment extends DS.Model {
author = DS.attr('string');
}
class Series extends DS.Model {
title = DS.attr('string');
}
class RelationalPost extends DS.Model {
title = DS.attr('string');
tag = DS.attr('string');
comments = DS.hasMany('comment', { async: true });
relatedPosts = DS.hasMany('post');
series = DS.belongsTo('series');
}
declare module 'ember-data' {
interface ModelRegistry {
'relational-post': RelationalPost;
comment: Comment;
series: Series;
}
}
let blogPost = store.peekRecord('relational-post', 1);
blogPost!.get('comments').then((comments) => {
// now we can work with the comments
let author: string = comments.get('firstObject')!.get('author');
});
blogPost!.hasMany('relatedPosts');
blogPost!.belongsTo('series');
+95
View File
@@ -0,0 +1,95 @@
import Ember from 'ember';
import DS from 'ember-data';
const JsonApi = DS.JSONAPISerializer.extend({});
const Customized = DS.JSONAPISerializer.extend({
serialize(snapshot: DS.Snapshot<'user'>, options: {}) {
const lookup = snapshot.belongsTo('username');
let json: any = this._super(...Array.from(arguments));
json.data.attributes.cost = {
amount: json.data.attributes.amount,
currency: json.data.attributes.currency
};
return json;
},
normalizeResponse(store: DS.Store, primaryModelClass: DS.Model, payload: any, id: string|number, requestType: string) {
payload.data.attributes.amount = payload.data.attributes.cost.amount;
payload.data.attributes.currency = payload.data.attributes.cost.currency;
delete payload.data.attributes.cost;
return this._super(...Array.from(arguments));
}
});
const EmbeddedRecordMixin = DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
author: {
serialize: false,
deserialize: 'records'
},
comments: {
deserialize: 'records',
serialize: 'ids'
}
}
});
class Message extends DS.Model.extend({
title: DS.attr(),
body: DS.attr(),
author: DS.belongsTo('user'),
comments: DS.belongsTo('comment')
}) {}
declare module 'ember-data' {
interface ModelRegistry {
'message-for-serializer': Message;
}
}
interface CustomSerializerOptions {
includeId: boolean;
}
const SerializerUsingSnapshots = DS.RESTSerializer.extend({
serialize(snapshot: DS.Snapshot<'message-for-serializer'>, options: CustomSerializerOptions) {
let json: any = {
POST_TTL: snapshot.attr('title'),
POST_BDY: snapshot.attr('body'),
POST_CMS: snapshot.hasMany('comments', { ids: true })
};
if (options.includeId) {
json.POST_ID_ = snapshot.id;
}
return json;
}
});
DS.Serializer.extend({
serialize(snapshot: DS.Snapshot<'message-for-serializer'>, options: {}) {
let json: any = {
id: snapshot.id
};
snapshot.eachAttribute((key, attribute) => {
json[key] = snapshot.attr(key);
});
snapshot.eachRelationship((key, relationship) => {
if (relationship.kind === 'belongsTo') {
json[key] = snapshot.belongsTo(key, { id: true });
} else if (relationship.kind === 'hasMany') {
json[key] = snapshot.hasMany(key, { ids: true });
}
});
return json;
},
});
+205
View File
@@ -0,0 +1,205 @@
import Ember from 'ember';
import DS from 'ember-data';
import { assertType } from './lib/assert';
declare const store: DS.Store;
class PostComment extends DS.Model {}
class Post extends DS.Model {
title = DS.attr('string');
comments = DS.hasMany('comment');
}
declare module 'ember-data' {
interface ModelRegistry {
'post': Post;
'post-comment': PostComment;
}
}
let post = store.createRecord('post', {
title: 'Rails is Omakase',
body: 'Lorem ipsum',
});
post.save(); // => POST to '/posts'
post.save().then(saved => {
assertType<Post>(saved);
});
store.findRecord('post', 1).then(function(post) {
post.get('title'); // => "Rails is Omakase"
post.set('title', 'A new post');
post.save(); // => PATCH to '/posts/1'
});
class User extends DS.Model {
username = DS.attr('string');
}
class Author extends User {}
declare module 'ember-data' {
interface ModelRegistry {
'user': User;
'author': Author;
}
}
store.queryRecord('user', {}).then(function(user) {
let username = user.get('username');
console.log(`Currently logged in as ${username}`);
});
store.findAll('post'); // => GET /posts
store.findAll('author', { reload: true }).then(function(authors) {
authors.getEach('id'); // ['first', 'second']
});
store.findAll('post', {
adapterOptions: { subscribe: false },
});
store.findAll('post', { include: 'comments,comments.author' });
store.peekAll('post'); // => no network request
if (store.hasRecordForId('post', 1)) {
let maybePost = store.peekRecord('post', 1);
if (maybePost) {
maybePost.get('id'); // 1
}
}
class Message extends DS.Model {
hasBeenSeen = DS.attr('boolean');
}
declare module 'ember-data' {
interface ModelRegistry {
message: Message;
}
}
const messages = store.peekAll('message');
messages.forEach(function(message) {
message.set('hasBeenSeen', true);
});
messages.save();
const people = store.peekAll('user');
people.get('isUpdating'); // false
people.update().then(function() {
people.get('isUpdating'); // false
});
people.get('isUpdating'); // true
const MyRoute = Ember.Route.extend({
model(params: any): any {
return this.store.findRecord('post', params.post_id, {
include: 'comments,comments.author',
});
},
});
// Store is injectable via `inject` and resolves to `DS.Store`.
const SomeComponent = Ember.Component.extend({
store: Ember.inject.service('store'),
lookUpUsers() {
assertType<User>(this.get('store').findRecord('user', 123));
assertType<DS.PromiseArray<User>>(this.get('store').findAll('user'));
}
});
const MyRouteAsync = Ember.Route.extend({
async beforeModel(): Promise<Ember.Array<DS.Model>> {
const store = Ember.get(this, 'store');
return await store.findAll('post-comment');
},
async model(): Promise<DS.Model> {
const store = this.get('store');
return await store.findRecord('post-comment', 1);
},
async afterModel(): Promise<Ember.Array<PostComment>> {
const post = await this.get('store').findRecord('post', 1);
return await post.get('comments');
}
});
class MyRouteAsyncES6 extends Ember.Route {
async beforeModel(): Promise<Ember.Array<DS.Model>> {
return await this.store.findAll('post-comment');
}
async model(): Promise<DS.Model> {
return await this.store.findRecord('post-comment', 1);
}
async afterModel(): Promise<Ember.Array<PostComment>> {
const post = await this.store.findRecord('post', 1);
return await post.get('comments');
}
}
// GET to /users?filter[email]=tomster@example.com
const tom = store
.query('user', {
filter: {
email: 'tomster@example.com',
},
})
.then(function(users) {
return users.get('firstObject');
});
// GET /users?isAdmin=true
const admins = store.query('user', { isAdmin: true });
admins.then(function() {
console.log(admins.get('length')); // 42
});
admins.update().then(function() {
admins.get('isUpdating'); // false
console.log(admins.get('length')); // 123
});
store.push({
data: [
{
id: 1,
type: 'album',
attributes: {
title: 'Fewer Moving Parts',
artist: 'David Bazan',
songCount: 10,
},
relationships: {},
},
{
id: 2,
type: 'album',
attributes: {
title: "Calgary b/w I Can't Make You Love Me/Nick Of Time",
artist: 'Bon Iver',
songCount: 2,
},
relationships: {},
},
],
});
class UserAdapter extends DS.Adapter {
thisAdapterOnlyMethod(): void {}
}
class UserSerializer extends DS.Serializer {
thisSerializerOnlyMethod(): void {}
}
declare module 'ember-data' {
interface AdapterRegistry {
user: UserAdapter;
}
interface SerializerRegistry {
user: UserSerializer;
}
}
assertType<UserAdapter>(store.adapterFor('user'));
assertType<UserSerializer>(store.serializerFor('user'));
+16
View File
@@ -0,0 +1,16 @@
import Ember from 'ember';
import DS from 'ember-data';
class Point extends Ember.Object {
x: number;
y: number;
}
const PointTransform = DS.Transform.extend({
serialize(value: Point): number[] {
return [value.get('x'), value.get('y')];
},
deserialize(value: [ number, number ]): Point {
return Point.create({ x: value[0], y: value[1] });
}
});
+2
View File
@@ -0,0 +1,2 @@
import DS from 'ember-data';
export default DS.Transform;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.BooleanTransform;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.DateTransform;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.NumberTransform;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.StringTransform;
+3
View File
@@ -0,0 +1,3 @@
import DS from 'ember-data';
export default DS.Transform;
+61
View File
@@ -0,0 +1,61 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"ember": ["ember/v2"],
"ember-data": ["ember-data/v2"],
"ember-data/*": ["ember-data/v2/*"]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true
},
"files": [
"index.d.ts",
"adapter.d.ts",
"serializer.d.ts",
"model.d.ts",
"attr.d.ts",
"relationships.d.ts",
"store.d.ts",
"transform.d.ts",
"adapters/errors.d.ts",
"adapters/rest.d.ts",
"adapters/json-api.d.ts",
"serializers/json-api.d.ts",
"serializers/json.d.ts",
"serializers/rest.d.ts",
"serializers/embedded-records-mixin.d.ts",
"transforms/transform.d.ts",
"transforms/date.d.ts",
"transforms/boolean.d.ts",
"transforms/string.d.ts",
"transforms/number.d.ts",
"test/lib/assert.ts",
"test/model.ts",
"test/module-api.ts",
"test/adapter.ts",
"test/serializer.ts",
"test/transform.ts",
"test/relationships.ts",
"test/store.ts",
"test/has-many.ts",
"test/belongs-to.ts",
"test/record-reference.ts",
"test/injections.ts",
"test/error.ts"
]
}
+20
View File
@@ -0,0 +1,20 @@
{
"extends": "dtslint/dt.json",
"rules": {
"strict-export-declare-modifiers": false,
// Heavy use of Function type in this older package.
"ban-types": false,
"jsdoc-format": false,
"no-misused-new": false,
// not sure what this means
"no-single-declare-module": false,
"object-literal-key-quotes": false,
"only-arrow-functions": false,
"no-empty-interface": false,
"prefer-const": false,
"no-unnecessary-generics": false,
"no-declare-current-package": false,
"no-self-import": false,
"no-return-await": false // used in tests
}
}
+3 -2
View File
@@ -1,8 +1,9 @@
// Type definitions for ember-feature-flags 3.0
// Type definitions for ember-feature-flags 4.0
// Project: https://github.com/kategengler/ember-feature-flags#readme
// Definitions by: Frank Tan <https://github.com/tansongyang>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
import Ember from 'ember';
@@ -0,0 +1,27 @@
import Features from 'ember-feature-flags';
import 'ember-feature-flags/tests/helpers/with-feature';
/** 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;
declare module 'ember-feature-flags' {
export default interface Features {
someFeature: boolean;
}
}
// https://www.npmjs.com/package/ember-feature-flags#withfeature
declare var features: Features;
features.isEnabled('new-billing-plans'); // $ExpectType boolean
features.enable('newHomepage'); // $ExpectType void
features.disable('newHomepage'); // $ExpectType void
const setup = {
'new-billing-plans': true,
'new-homepage': false
};
features.setup(setup); // $ExpectType void
withFeature('new-homepage'); // $ExpectType void
assertType<boolean>(features.get('someFeature'));
+16
View File
@@ -0,0 +1,16 @@
// Type definitions for ember-feature-flags 3.0
// Project: https://github.com/kategengler/ember-feature-flags#readme
// Definitions by: Frank Tan <https://github.com/tansongyang>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import Ember from 'ember';
// https://github.com/kategengler/ember-feature-flags/blob/v3.0.0/addon/services/features.js#L5
export default interface Features extends Ember.Service {
setup(features: { [key: string]: boolean }): void;
enable(feature: string): void;
disable(feature: string): void;
isEnabled(feature: string): boolean;
}
@@ -0,0 +1,3 @@
// https://www.npmjs.com/package/ember-feature-flags#withfeature
// https://github.com/kategengler/ember-feature-flags/blob/v3.0.0/test-support/helpers/with-feature.js#L3
declare function withFeature(name: string): void;
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"ember": ["ember/v2"],
"ember-feature-flags": ["ember-feature-flags/v3"]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"tests/helpers/with-feature.d.ts",
"ember-feature-flags-tests.ts"
]
}
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "dtslint/dt.json",
"rules": {
"strict-export-declare-modifiers": false
}
}
+2 -1
View File
@@ -2,8 +2,9 @@
// Project: https://github.com/emberjs/ember-mocha#readme
// Definitions by: Derek Wickern <https://github.com/dwickern>
// Simon Ihmig <https://github.com/simonihmig>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
import { TestContext, ModuleCallbacks } from "ember-test-helpers";
import Ember from 'ember';
+3 -2
View File
@@ -1,8 +1,9 @@
// Type definitions for ember-modal-dialog 2.4
// Type definitions for ember-modal-dialog 3.0
// Project: https://github.com/yapplabs/ember-modal-dialog#readme
// Definitions by: Frank Tan <https://github.com/tansongyang>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
declare module 'ember-modal-dialog/components/modal-dialog' {
import Ember from 'ember';
@@ -0,0 +1,27 @@
import ModalDialog from 'ember-modal-dialog/components/modal-dialog';
class MyDialog extends ModalDialog {
// https://www.npmjs.com/package/ember-modal-dialog#configurable-properties
testProperties() {
this.hasOverlay; // $ExpectType boolean
this.translucentOverlay; // $ExpectType boolean
this.onClose();
this.onClickOverlay();
this.clickOutsideToClose; // $ExpectType boolean
this.renderInPlace; // $ExpectType boolean
this.overlayPosition; // $ExpectType "parent" | "sibling"
this.containerClass; // $ExpectType string
this.containerClassNames; // $ExpectType string[]
this.overlayClass; // $ExpectType string
this.overlayClassNames; // $ExpectType string[]
this.wrapperClass; // $ExpectType string
this.wrapperClassNames; // $ExpectType string[]
this.animatable; // $ExpectType boolean
}
}
class MyOtherDialog extends ModalDialog.extend({
testProperties() {
this.hasOverlay; // $ExpectType boolean
}
}) {}
+94
View File
@@ -0,0 +1,94 @@
// Type definitions for ember-modal-dialog 2.4
// Project: https://github.com/yapplabs/ember-modal-dialog#readme
// Definitions by: Frank Tan <https://github.com/tansongyang>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
declare module 'ember-modal-dialog/components/modal-dialog' {
import Ember from 'ember';
// https://github.com/yapplabs/ember-modal-dialog/blob/v2.4.1/addon/components/modal-dialog.js#L28
// https://www.npmjs.com/package/ember-modal-dialog#configurable-properties
export default class ModalDialog extends Ember.Component {
/**
* Toggles presence of overlay div in DOM
*/
hasOverlay: boolean;
/**
* Indicates translucence of overlay, toggles presence of translucent CSS
* selector
*/
translucentOverlay: boolean;
/**
* The action handler for the dialog's onClose action. This action triggers
* when the user clicks the modal overlay.
*/
onClose: () => void;
/**
* An action to be called when the overlay is clicked. If this action is
* specified, clicking the overlay will invoke it instead of onClose.
*/
onClickOverlay: () => void;
/**
* Indicates whether clicking outside a modal without an overlay should
* close the modal. Useful if your modal isn't the focus of interaction, and
* you want hover effects to still work outside the modal.
*/
clickOutsideToClose: boolean;
/**
* A boolean, when true renders the modal without wormholing or tethering,
* useful for including a modal in a style guide
*/
renderInPlace: boolean;
/**
* either 'parent' or 'sibling', to control whether the overlay div is
* rendered as a parent element of the container div or as a sibling to it
* (default: 'parent')
*/
overlayPosition: 'parent' | 'sibling';
/**
* CSS class name(s) to append to container divs. Set this from template.
*/
containerClass: string;
/**
* CSS class names to append to container divs. This is a concatenated
* property, so it does not replace the default container class
* (default: 'ember-modal-dialog'. If you subclass this component, you may
* define this in your subclass.)
*/
containerClassNames: string[];
/**
* CSS class name(s) to append to overlay divs. Set this from template.
*/
overlayClass: string;
/**
* CSS class names to append to overlay divs. This is a concatenated
* property, so it does not replace the default overlay class
* (default: 'ember-modal-overlay'. If you subclass this component, you may
* define this in your subclass.)
*/
overlayClassNames: string[];
/**
* CSS class name(s) to append to wrapper divs. Set this from template.
*/
wrapperClass: string;
/**
* CSS class names to append to wrapper divs. This is a concatenated
* property, so it does not replace the default container class
* (default: 'ember-modal-wrapper'. If you subclass this component, you may
* define this in your subclass.)
*/
wrapperClassNames: string[];
/**
* A boolean, when true makes modal animatable using liquid-fire
* (requires liquid-wormhole to be installed, and for tethering situations
* liquid-tether. Having these optional dependencies installed and NOT
* explicitly specifying animatable is deprecated in 2.x and is equivalent
* to animatable=false for backwards compatibility. As of 3.x, the implicit
* default will be animatable=true when the optional
* liquid-wormhole/liquid-tether dependency is present.
*/
animatable: boolean;
}
}
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"ember-modal-dialog": ["ember-modal-dialog/v2"],
"ember": ["ember/v2"]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ember-modal-dialog-tests.ts"
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "dtslint/dt.json",
"rules": {
"no-single-declare-module": false,
"no-declare-current-package": false,
"strict-export-declare-modifiers": false
}
}
+3 -2
View File
@@ -1,8 +1,9 @@
// Type definitions for ember-qunit 3.0
// Type definitions for ember-qunit 3.4
// Project: https://github.com/emberjs/ember-qunit#readme
// Definitions by: Derek Wickern <https://github.com/dwickern>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
/// <reference types="qunit" />
+1
View File
@@ -1,6 +1,7 @@
// Type definitions for ember-qunit 2.2
// Project: https://github.com/emberjs/ember-qunit#readme
// Definitions by: Derek Wickern <https://github.com/dwickern>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
+4 -2
View File
@@ -16,7 +16,9 @@
"paths": {
"ember-qunit": [
"ember-qunit/v2"
]
],
"ember": ["ember/v2"],
"ember-test-helpers": ["ember-test-helpers/v0"]
},
"types": [],
"noEmit": true,
@@ -26,4 +28,4 @@
"index.d.ts",
"ember-qunit-tests.ts"
]
}
}
+3 -2
View File
@@ -1,8 +1,9 @@
// Type definitions for ember-resolver 4.5
// Type definitions for ember-resolver 5.0
// Project: https://github.com/ember-cli/ember-resolver#readme
// Definitions by: Dan Freeman <https://github.com/dfreeman>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
/// <reference types="ember" />
@@ -0,0 +1,12 @@
import Application from '@ember/application';
import EmberResolver from 'ember-resolver';
const MyResolver = EmberResolver.extend({
pluralizedTypes: {
sheep: 'sheep'
}
});
const App = Application.extend({
Resolver: MyResolver
});
+15
View File
@@ -0,0 +1,15 @@
// Type definitions for ember-resolver 4.5
// Project: https://github.com/ember-cli/ember-resolver#readme
// Definitions by: Dan Freeman <https://github.com/dfreeman>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
/// <reference types="ember" />
import Resolver from '@ember/application/resolver';
/**
* An Ember `Resolver` implementation used by ember-cli.
*/
export default class EmberResolver extends Resolver {}
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"ember": ["ember/v2"],
"ember-resolver": ["ember-resolver/v4"]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ember-resolver-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }
+11 -2
View File
@@ -1,8 +1,17 @@
// Type definitions for ember-test-helpers 0.7
// Type definitions for ember-test-helpers 1.0
// Project: https://github.com/emberjs/ember-test-helpers#readme
// Definitions by: Derek Wickern <https://github.com/dwickern>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
// NOTE: These types apply to ember-test-helper v0.7. The major
// version had to be bumped for SemVer due to a breaking change
// in TypeScript 3.1
//
// In the future, we'll use another versioning strategy that
// provides safety from breaking changes without bumping the major
// version number
/// <reference types="jquery" />
@@ -0,0 +1,53 @@
/// <reference types="qunit" />
import { ModuleCallbacks, TestContext, TestModule } from "ember-test-helpers";
import wait from 'ember-test-helpers/wait';
import hasEmberVersion from 'ember-test-helpers/has-ember-version';
import hbs from 'htmlbars-inline-precompile';
function moduleFor(name: string, description: string, callbacks: ModuleCallbacks) {
const module = new TestModule(name, description, callbacks);
QUnit.module(module.name, {
beforeEach() {
module.setup();
},
afterEach() {
module.teardown();
}
});
}
async function testWait() {
await wait();
}
if (hasEmberVersion(2, 10)) {
// ...
}
// https://github.com/emberjs/ember-test-helpers/blob/f07e86914f2a3823c4cb6787307f9ba2bf447e68/tests/unit/setup-context-test.js
QUnit.test('it sets up this.owner', function(this: TestContext, assert: Assert) {
const { owner } = this;
assert.ok(owner, 'owner was setup');
assert.equal(typeof owner.lookup, 'function', 'has expected lookup interface');
if (hasEmberVersion(2, 12)) {
assert.equal(typeof owner.factoryFor, 'function', 'has expected factory interface');
}
});
QUnit.test('can pauseTest to be resumed "later"', async function(this: TestContext, assert: Assert) {
const promise = this.pauseTest();
this.resumeTest();
await promise;
});
// https://github.com/emberjs/ember-test-helpers/blob/fb4c8d4cd36b54728ce180227f865b1fa0162632/tests/unit/setup-rendering-context-test.js
QUnit.test('render exposes an `.element` property', async function(this: TestContext, assert: Assert) {
await this.render(hbs`<p>Hello!</p>`);
assert.equal(this.element.textContent, 'Hello!');
});
+100
View File
@@ -0,0 +1,100 @@
// Type definitions for ember-test-helpers 0.7
// Project: https://github.com/emberjs/ember-test-helpers#readme
// Definitions by: Derek Wickern <https://github.com/dwickern>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
/// <reference types="jquery" />
declare module 'ember-test-helpers' {
import Ember from 'ember';
import { TemplateFactory } from 'htmlbars-inline-precompile';
import RSVP from "rsvp";
interface ModuleCallbacks {
integration?: boolean;
unit?: boolean;
needs?: string[];
beforeSetup?(assert?: any): void;
setup?(assert?: any): void;
teardown?(assert?: any): void;
afterTeardown?(assert?: any): void;
[key: string]: any;
}
interface TestContext {
get(key: string): any;
getProperties<K extends string>(...keys: K[]): Pick<any, K>;
set<V>(key: string, value: V): V;
setProperties<P extends { [key: string]: any }>(hash: P): P;
on(actionName: string, handler: (this: TestContext, ...args: any[]) => any): void;
send(actionName: string): void;
$: JQueryStatic;
subject(options?: {}): any;
render(template?: string | string[] | TemplateFactory): Promise<void>;
clearRender(): void;
registry: Ember.Registry;
container: Ember.Container;
dispatcher: Ember.EventDispatcher;
application: Ember.Application;
register(fullName: string, factory: any): void;
factory(fullName: string): any;
inject: {
controller(name: string, options?: { as: string }): any;
service(name: string, options?: { as: string }): any;
};
owner: Ember.ApplicationInstance & {
factoryFor(fullName: string, options?: {}): any;
};
pauseTest(): Promise<void>;
resumeTest(): void;
element: Element;
}
class TestModule {
constructor(name: string, callbacks?: ModuleCallbacks);
constructor(name: string, description?: string, callbacks?: ModuleCallbacks);
name: string;
subjectName: string;
description: string;
isIntegration: boolean;
callbacks: ModuleCallbacks;
context: TestContext;
resolver: Ember.Resolver;
setup(assert?: any): RSVP.Promise<void>;
teardown(assert?: any): RSVP.Promise<void>;
getContext(): TestContext;
setContext(context: TestContext): void;
}
class TestModuleForAcceptance extends TestModule {}
class TestModuleForIntegration extends TestModule {}
class TestModuleForComponent extends TestModule {}
class TestModuleForModel extends TestModule {}
function getContext(): TestContext | undefined;
function setContext(context: TestContext): void;
function unsetContext(): void;
function setResolver(resolver: Ember.Resolver): void;
}
declare module 'ember-test-helpers/wait' {
import RSVP from "rsvp";
interface WaitOptions {
waitForTimers?: boolean;
waitForAJAX?: boolean;
waitForWaiters?: boolean;
}
export default function wait(options?: WaitOptions): RSVP.Promise<void>;
}
declare module 'ember-test-helpers/has-ember-version' {
export default function hasEmberVersion(major: number, minor: number): boolean;
}
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"ember": ["ember/v2"],
"ember-test-helpers": ["ember-test-helpers/v0"]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ember-test-helpers-tests.ts"
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "dtslint/dt.json",
"rules": {
"strict-export-declare-modifiers": false,
"no-duplicate-imports": false,
"no-declare-current-package": false
}
}
+72 -72
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>
@@ -9,7 +9,7 @@
// Alex LaFroscia <https://github.com/alexlafroscia>
// Mike North <https://github.com/mike-north>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.8
/// <reference types="jquery" />
/// <reference types="handlebars" />
@@ -30,8 +30,19 @@ declare module 'ember' {
/**
* Deconstructs computed properties into the types which would be returned by `.get()`.
*/
type ComputedPropertyGetters<T> = { [K in keyof T]: Ember.ComputedProperty<T[K], any> | ModuleComputed<T[K], any> | T[K] };
type ComputedPropertySetters<T> = { [K in keyof T]: Ember.ComputedProperty<any, T[K]> | ModuleComputed<any, T[K]> | T[K] };
type UnwrapComputedPropertyGetter<T> =
T extends Ember.ComputedProperty<infer U, any> ? U :
T;
type UnwrapComputedPropertyGetters<T> = {
[P in keyof T]: UnwrapComputedPropertyGetter<T[P]>;
};
type UnwrapComputedPropertySetter<T> =
T extends Ember.ComputedProperty<any, infer U> ? U :
T;
type UnwrapComputedPropertySetters<T> = {
[P in keyof T]: UnwrapComputedPropertySetter<T[P]>;
};
/**
* Check that any arguments to `create()` match the type's properties.
@@ -698,6 +709,10 @@ declare module 'ember' {
This will force the cached result to be recomputed if the dependencies are modified.
**/
class ComputedProperty<Get, Set = Get> {
// Necessary in order to avoid losing type information
// see: https://github.com/typed-ember/ember-cli-typescript/issues/246#issuecomment-414812013
private ______getType: Get;
private ______setType: Set;
/**
* Call on a computed property to set it into non-cached mode. When in this
* mode the computed property will not automatically cache the return value.
@@ -833,36 +848,31 @@ declare module 'ember' {
**/
toString(): string;
static create<Instance>(this: EmberClassConstructor<Instance>): Fix<Instance>;
static create<Class extends typeof Ember.CoreObject>(this: Class): InstanceType<Class>;
static create<Instance, Args, T1 extends EmberInstanceArguments<Args>>(
this: EmberClassConstructor<Instance & ComputedPropertyGetters<Args>>,
arg1: T1 & ThisType<Fix<T1 & Instance>>
): Fix<Instance & T1>;
static create<Class extends typeof Ember.CoreObject,
T1 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>
>(this: Class,
arg1: T1 & ThisType<T1 & InstanceType<Class>>
): InstanceType<Class> & T1;
static create<
Instance,
Args,
T1 extends EmberInstanceArguments<Args>,
T2 extends EmberInstanceArguments<Args>
>(
this: EmberClassConstructor<Instance & ComputedPropertyGetters<Args>>,
arg1: T1 & ThisType<Fix<Instance & T1>>,
arg2: T2 & ThisType<Fix<Instance & T1 & T2>>
): Fix<Instance & T1 & T2>;
static create<Class extends typeof Ember.CoreObject,
T1 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>,
T2 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>
>(this: Class,
arg1: T1 & ThisType<T1 & InstanceType<Class>>,
arg2: T2 & ThisType<T2 & InstanceType<Class>>
): InstanceType<Class> & T1 & T2;
static create<
Instance,
Args,
T1 extends EmberInstanceArguments<Args>,
T2 extends EmberInstanceArguments<Args>,
T3 extends EmberInstanceArguments<Args>
>(
this: EmberClassConstructor<Instance & ComputedPropertyGetters<Args>>,
arg1: T1 & ThisType<Fix<Instance & T1>>,
arg2: T2 & ThisType<Fix<Instance & T1 & T2>>,
arg3: T3 & ThisType<Fix<Instance & T1 & T2 & T3>>
): Fix<Instance & T1 & T2 & T3>;
static create<Class extends typeof Ember.CoreObject,
T1 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>,
T2 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>,
T3 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>
>(this: Class,
arg1: T1 & ThisType<T1 & InstanceType<Class>>,
arg2: T2 & ThisType<T2 & InstanceType<Class>>,
arg3: T3 & ThisType<T3 & InstanceType<Class>>
): InstanceType<Class> & T1 & T2 & T3;
static extend<Statics, Instance>(
this: Statics & EmberClassConstructor<Instance>
@@ -1677,29 +1687,27 @@ declare module 'ember' {
/**
* Retrieves the value of a property from the object.
*/
get<T, K extends keyof T>(this: ComputedPropertyGetters<T>, key: K): T[K];
get<K extends keyof this>(key: K): UnwrapComputedPropertyGetter<this[K]>;
/**
* To get the values of multiple properties at once, call `getProperties`
* with a list of strings or an array:
*/
getProperties<T, K extends keyof T>(this: ComputedPropertyGetters<T>, list: K[]): Pick<T, K>;
getProperties<T, K extends keyof T>(
this: ComputedPropertyGetters<T>,
getProperties<K extends keyof this>(list: K[]): Pick< UnwrapComputedPropertyGetters<this>, K>;
getProperties<K extends keyof this>(
...list: K[]
): Pick<T, K>;
): Pick< UnwrapComputedPropertyGetters<this>, K>;
/**
* Sets the provided key or path to the value.
*/
set<T, K extends keyof T>(this: ComputedPropertySetters<T>, key: K, value: T[K]): T[K];
set<K extends keyof this>(key: K, value: UnwrapComputedPropertySetter<this[K]>): UnwrapComputedPropertySetter<this[K]>;
/**
* Sets a list of properties at once. These properties are set inside
* a single `beginPropertyChanges` and `endPropertyChanges` batch, so
* observers will be buffered.
*/
setProperties<T, K extends keyof T>(
this: ComputedPropertySetters<T>,
hash: Pick<T, K>
): Pick<T, K>;
setProperties<K extends keyof this>(
hash: Pick<UnwrapComputedPropertySetters<this>, K>
): Pick< UnwrapComputedPropertySetters<this>, K>;
/**
* Convenience method to call `propertyWillChange` and `propertyDidChange` in
* succession.
@@ -1735,11 +1743,10 @@ declare module 'ember' {
* Retrieves the value of a property, or a default value in the case that the
* property returns `undefined`.
*/
getWithDefault<T, K extends keyof T>(
this: ComputedPropertyGetters<T>,
getWithDefault<K extends keyof this>(
key: K,
defaultValue: T[K]
): T[K];
defaultValue: UnwrapComputedPropertyGetter<this[K]>
): UnwrapComputedPropertyGetter<this[K]>;
/**
* Set the value of a property to the current value plus some amount.
*/
@@ -1759,7 +1766,7 @@ declare module 'ember' {
* without accidentally invoking it if it is intended to be
* generated lazily.
*/
cacheFor<T, K extends keyof T>(this: ComputedPropertyGetters<T>, key: K): T[K] | undefined;
cacheFor<K extends keyof this>(key: K): UnwrapComputedPropertyGetter<this[K]> | undefined;
}
const Observable: Mixin<Observable, Ember.CoreObject>;
/**
@@ -3046,9 +3053,9 @@ declare module 'ember' {
* it to be created.
*/
function cacheFor<T, K extends keyof T>(
obj: ComputedPropertyGetters<T>,
obj: T,
key: K
): T[K] | undefined;
): UnwrapComputedPropertyGetter<T[K]> | undefined;
/**
* Add an event listener
*/
@@ -3094,16 +3101,11 @@ declare module 'ember' {
* To get multiple properties at once, call `Ember.getProperties`
* with an object followed by a list of strings or an array:
*/
function getProperties<T, K extends keyof T>(obj: T, list: K[]): Pick<UnwrapComputedPropertyGetters<T>, K>; // for dynamic K
function getProperties<T, K extends keyof T>(
obj: ComputedPropertyGetters<T>,
list: K[]
): Pick<T, K>;
function getProperties<T, K extends keyof T>(obj: T, list: K[]): Pick<T, K>; // for dynamic K
function getProperties<T, K extends keyof T>(
obj: ComputedPropertyGetters<T>,
obj: T,
...list: K[]
): Pick<T, K>;
function getProperties<T, K extends keyof T>(obj: T, ...list: K[]): Pick<T, K>; // for dynamic K
): Pick<UnwrapComputedPropertyGetters<T>, K>;
/**
* A value is blank if it is empty or a whitespace string.
*/
@@ -3197,30 +3199,27 @@ declare module 'ember' {
* the function will be invoked. If the property is not defined but the
* object implements the `unknownProperty` method then that will be invoked.
*/
function get<T, K extends keyof T>(obj: ComputedPropertyGetters<T>, key: K): T[K];
function get<T, K extends keyof T>(obj: T, key: K): T[K]; // for dynamic K
function get<T, K extends keyof T>(obj: T, key: K): UnwrapComputedPropertyGetter<T[K]>;
/**
* Retrieves the value of a property from an Object, or a default value in the
* case that the property returns `undefined`.
*/
function getWithDefault<T, K extends keyof T>(
obj: ComputedPropertyGetters<T>,
obj: T,
key: K,
defaultValue: T[K]
): T[K];
function getWithDefault<T, K extends keyof T>(obj: T, key: K, defaultValue: T[K]): T[K]; // for dynamic K
defaultValue: UnwrapComputedPropertyGetter<T[K]>
): UnwrapComputedPropertyGetter<T[K]>;
/**
* Sets the value of a property on an object, respecting computed properties
* and notifying observers and other listeners of the change. If the
* property is not defined but the object implements the `setUnknownProperty`
* method then that will be invoked as well.
*/
function set<T, K extends keyof T, V extends T[K]>(
obj: ComputedPropertySetters<T>,
function set<T, K extends keyof T>(
obj: T,
key: K,
value: V
): V;
function set<T, K extends keyof T, V extends T[K]>(obj: T, key: K, value: V): V; // for dynamic K
value: UnwrapComputedPropertySetter<T[K]>
): UnwrapComputedPropertyGetter<T[K]>;
/**
* Error-tolerant form of `Ember.set`. Will not blow up if any part of the
* chain is `undefined`, `null`, or destroyed.
@@ -3232,10 +3231,9 @@ declare module 'ember' {
* observers will be buffered.
*/
function setProperties<T, K extends keyof T>(
obj: ComputedPropertySetters<T>,
hash: Pick<T, K>
): Pick<T, K>;
function setProperties<T, K extends keyof T>(obj: T, hash: Pick<T, K>): Pick<T, K>; // for dynamic K
obj: T,
hash: Pick<UnwrapComputedPropertySetters<T>, K>
): Pick<UnwrapComputedPropertyGetters<T>, K>;
/**
* Detects when a specific package of Ember (e.g. 'Ember.Application')
* has fully loaded and is available for extension.
@@ -3674,7 +3672,9 @@ declare module '@ember/object' {
declare module '@ember/object/computed' {
import Ember from 'ember';
export default class ComputedProperty<Get, Set = Get> extends Ember.ComputedProperty<Get, Set> { }
type ComputedProperty<Get, Set = Get> = Ember.ComputedProperty<Get, Set>;
const ComputedProperty: typeof Ember.ComputedProperty;
export default ComputedProperty;
export const alias: typeof Ember.computed.alias;
export const and: typeof Ember.computed.and;
export const bool: typeof Ember.computed.bool;
+20
View File
@@ -0,0 +1,20 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
class Foo extends Ember.Object {
hello() { return 'world'; }
protected bar() { return 'bar'; }
private baz() { return 'baz'; }
}
const f = new Foo();
assertType<string>(f.hello());
assertType<string>(f.bar()); // $ExpectError
assertType<string>(f.baz()); // $ExpectError
class Foo2 extends Ember.Object.extend({
bar: ''
}) {
hello() { return 'world'; }
protected bar() { return 'bar'; } // $ExpectError
private baz() { return 'baz'; }
}
+11
View File
@@ -0,0 +1,11 @@
import { assertType } from './lib/assert';
import Ember from 'ember';
import { PersonWithNumberName, Person } from './create';
Person.create({ firstName: 99 }); // $ExpectError
Person.create({}, { firstName: 99 }); // $ExpectError
Person.create({}, {}, { firstName: 99 }); // $ExpectError
const p4 = new PersonWithNumberName();
// assertType<Ember.ComputedProperty<string, string>>(p4.fullName); // $ExpectError
+48 -1
View File
@@ -1,7 +1,54 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
/**
* Zero-argument case
*/
const o = Ember.Object.create();
// create returns an object
assertType<object>(o);
// object returned by create type-checks as an instance of Ember.Object
assertType<boolean>(o.isDestroyed); // from instance
assertType<boolean>(o.isDestroying); // from instance
assertType<(key: string) => any>(o.get); // from prototype
/**
* One-argument case
*/
const o1 = Ember.Object.create({x: 9, y: 'hello', z: false});
assertType<number>(o1.x);
assertType<string>(o1.y);
o1.y; // $ExpectType string
o1.z; // $ExpectType boolean
const obj = Ember.Object.create({ a: 1 }, { b: 2 }, { c: 3 });
assertType<number>(obj.a);
assertType<number>(obj.b);
assertType<number>(obj.a);
assertType<number>(obj.c);
export class Person extends Ember.Object.extend({
fullName: Ember.computed('firstName', 'lastName', function() {
return [this.firstName + this.lastName].join(' ');
})
}) {
firstName: string;
lastName: string;
age: number;
}
const p = new Person();
assertType<string>(p.firstName);
assertType<Ember.ComputedProperty<string>>(p.fullName);
assertType<string>(p.get('fullName'));
const p2 = Person.create({ firstName: 'string' });
const p2b = Person.create({}, { firstName: 'string' });
const p2c = Person.create({}, {}, { firstName: 'string' });
export class PersonWithNumberName extends Person.extend({
fullName: 6
}) {}
const p4 = new PersonWithNumberName();
assertType<string>(p4.firstName);
assertType<number>(p4.fullName);
+3
View File
@@ -29,6 +29,9 @@ assertType<string>(Person2.species);
let tom = Person2.create({
name: 'Tom Dale'
});
let badTom = Person2.create({ name: 99 }); // $ExpectError
let yehuda = Person2.createPerson('Yehuda Katz');
tom.sayHello(); // "Hello. My name is Tom Dale"
+3
View File
@@ -22,6 +22,7 @@
"index.d.ts",
"test/lib/assert.ts",
"test/application.ts",
"test/access-modifier.ts",
"test/application-instance.ts",
"test/engine-instance.ts",
"test/ember-tests.ts",
@@ -29,6 +30,8 @@
"test/event.ts",
"test/extend.ts",
"test/create.ts",
"test/create-negative.ts",
"test/create.ts",
"test/object.ts",
"test/observable.ts",
"test/mixin.ts",
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
}
});
+26
View File
@@ -0,0 +1,26 @@
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'));
// This test must be disabled due to a breaking change in TS 3.1
// see: https://github.com/Microsoft/TypeScript/issues/26120
// https://github.com/typed-ember/ember-cli-typescript/issues/246
// https://github.com/Microsoft/TypeScript/pull/26063
//
// 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');
}
});
+9
View File
@@ -0,0 +1,9 @@
import { assertType } from './lib/assert';
import Ember from 'ember';
import { PersonWithNumberName, Person } from './create';
const p3 = Person.create({ firstName: 99 }); // $ExpectError
const p2b = Person.create({}, { firstName: 99 }); // $ExpectError
const p2c = Person.create({}, {}, { firstName: 99 }); // $ExpectError
const p4 = new PersonWithNumberName();
+39
View File
@@ -0,0 +1,39 @@
import Ember from 'ember';
import { assertType } from './lib/assert';
const o = Ember.Object.create();
assertType<object>(o);
const o1 = Ember.Object.create({x: 9});
assertType<number>(o1.x);
const obj = Ember.Object.create({ a: 1 }, { b: 2 }, { c: 3 });
assertType<number>(obj.b);
assertType<number>(obj.a);
assertType<number>(obj.c);
export class Person extends Ember.Object.extend({
fullName: Ember.computed('firstName', 'lastName', function() {
return [this.firstName + this.lastName].join(' ');
})
}) {
firstName: string;
lastName: string;
age: number;
}
const p = new Person();
assertType<string>(p.firstName);
assertType<Ember.ComputedProperty<string>>(p.fullName);
assertType<string>(p.get('fullName'));
const p2 = Person.create({ firstName: 'string' });
const p2b = Person.create({}, { firstName: 'string' });
const p2c = Person.create({}, {}, { firstName: 'string' });
export class PersonWithNumberName extends Person.extend({
fullName: 6
}) {}
const p4 = new PersonWithNumberName();
assertType<string>(p4.firstName);
assertType<number>(p4.fullName);
+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')));

Some files were not shown because too many files have changed in this diff Show More