diff --git a/types/ember-data/index.d.ts b/types/ember-data/index.d.ts index b071662326..b038a7f62b 100644 --- a/types/ember-data/index.d.ts +++ b/types/ember-data/index.d.ts @@ -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 // Mike North // Chris Krycho // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 2.8 declare module 'ember-data' { import Ember from 'ember'; diff --git a/types/ember-data/v2/adapter.d.ts b/types/ember-data/v2/adapter.d.ts new file mode 100644 index 0000000000..e7d126b345 --- /dev/null +++ b/types/ember-data/v2/adapter.d.ts @@ -0,0 +1,4 @@ +import { DS } from "ember-data"; + +export default DS.Adapter; +export { AdapterRegistry } from 'ember-data'; diff --git a/types/ember-data/v2/adapters/errors.d.ts b/types/ember-data/v2/adapters/errors.d.ts new file mode 100644 index 0000000000..bd7e1dd65a --- /dev/null +++ b/types/ember-data/v2/adapters/errors.d.ts @@ -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; diff --git a/types/ember-data/v2/adapters/json-api.d.ts b/types/ember-data/v2/adapters/json-api.d.ts new file mode 100644 index 0000000000..4296b32eec --- /dev/null +++ b/types/ember-data/v2/adapters/json-api.d.ts @@ -0,0 +1,2 @@ +import DS from 'ember-data'; +export default DS.JSONAPIAdapter; diff --git a/types/ember-data/v2/adapters/rest.d.ts b/types/ember-data/v2/adapters/rest.d.ts new file mode 100644 index 0000000000..c7dd3824ae --- /dev/null +++ b/types/ember-data/v2/adapters/rest.d.ts @@ -0,0 +1,2 @@ +import DS from 'ember-data'; +export default DS.RESTAdapter; diff --git a/types/ember-data/v2/attr.d.ts b/types/ember-data/v2/attr.d.ts new file mode 100644 index 0000000000..2ecdea2d7e --- /dev/null +++ b/types/ember-data/v2/attr.d.ts @@ -0,0 +1,2 @@ +import DS from 'ember-data'; +export default DS.attr; diff --git a/types/ember-data/v2/index.d.ts b/types/ember-data/v2/index.d.ts new file mode 100644 index 0000000000..dccf63e890 --- /dev/null +++ b/types/ember-data/v2/index.d.ts @@ -0,0 +1,2121 @@ +// Type definitions for ember-data 2.14 +// Project: https://github.com/emberjs/data +// Definitions by: Derek Wickern +// Mike North +// Chris Krycho +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.4 + +import Ember from 'ember'; +import RSVP from 'rsvp'; + +export interface ModelRegistry {} +export interface AdapterRegistry {} +export interface SerializerRegistry {} +export interface TransformRegistry { + 'string': string; + 'boolean': boolean; + 'number': number; + 'date': Date; +} + +type AttributesFor = keyof Model; // TODO: filter to attr properties only (TS 2.8) +type RelationshipsFor = keyof Model; // TODO: filter to hasMany/belongsTo properties only (TS 2.8) + +export interface ChangedAttributes { + [key: string]: [any, any] | undefined; +} +interface AttributeMeta { + type: keyof TransformRegistry; + options: object; + name: AttributesFor; + parentType: Model; + isAttribute: true; +} +interface RelationshipMeta { + key: RelationshipsFor; + kind: 'belongsTo' | 'hasMany'; + type: keyof ModelRegistry; + options: object; + name: string; + parentType: Model; + isRelationship: true; +} + +export namespace DS { + /** + * Convert an hash of errors into an array with errors in JSON-API format. + */ + function errorsHashToArray(errors: {}): any[]; + /** + * Convert an array of errors in JSON-API format into an object. + */ + function errorsArrayToHash(errors: any[]): {}; + + interface RelationshipOptions { + async?: boolean; + inverse?: RelationshipsFor | null; + polymorphic?: boolean; + } + + interface Sync { async: false; } + interface Async { async?: true; } + + /** + * `DS.belongsTo` is used to define One-To-One and One-To-Many + * relationships on a [DS.Model](/api/data/classes/DS.Model.html). + */ + function belongsTo( + modelName: K, + options: RelationshipOptions & Sync + ): Ember.ComputedProperty; + function belongsTo( + modelName: K, + options?: RelationshipOptions & Async + ): Ember.ComputedProperty, ModelRegistry[K]>; + /** + * `DS.hasMany` is used to define One-To-Many and Many-To-Many + * relationships on a [DS.Model](/api/data/classes/DS.Model.html). + */ + function hasMany( + type: K, + options: RelationshipOptions & Sync + ): Ember.ComputedProperty>; + function hasMany( + type: K, + options?: RelationshipOptions & Async + ): Ember.ComputedProperty, Ember.Array>; + /** + * This method normalizes a modelName into the format Ember Data uses + * internally. + */ + function normalizeModelName(modelName: K): string; + const VERSION: string; + + interface AttrOptions { + defaultValue?: T | (() => T); + allowNull?: boolean; // TODO: restrict to boolean transform (TS 2.8) + } + + /** + * `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html). + * By default, attributes are passed through as-is, however you can specify an + * optional type to have the value automatically transformed. + * Ember Data ships with four basic transform types: `string`, `number`, + * `boolean` and `date`. You can define your own transforms by subclassing + * [DS.Transform](/api/data/classes/DS.Transform.html). + */ + function attr( + type: K, + options?: AttrOptions + ): Ember.ComputedProperty; + function attr(options?: AttrOptions): Ember.ComputedProperty; + /** + * WARNING: This interface is likely to change in order to accomodate https://github.com/emberjs/rfcs/pull/4 + * ## Using BuildURLMixin + * To use url building, include the mixin when extending an adapter, and call `buildURL` where needed. + * The default behaviour is designed for RESTAdapter. + * ### Example + * ```javascript + * export default DS.Adapter.extend(BuildURLMixin, { + * findRecord: function(store, type, id, snapshot) { + * var url = this.buildURL(type.modelName, id, snapshot, 'findRecord'); + * return this.ajax(url, 'GET'); + * } + * }); + * ``` + * ### Attributes + * The `host` and `namespace` attributes will be used if defined, and are optional. + */ + class BuildURLMixin { + /** + * Builds a URL for a given type and optional ID. + */ + buildURL( + modelName?: K, + id?: string | any[] | {} | null, + snapshot?: Snapshot | any[] | null, + requestType?: string, + query?: {} + ): string; + /** + * Builds a URL for a `store.findRecord(type, id)` call. + */ + urlForFindRecord( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for a `store.findAll(type)` call. + */ + urlForFindAll( + modelName: K, + snapshot: SnapshotRecordArray + ): string; + /** + * Builds a URL for a `store.query(type, query)` call. + */ + urlForQuery(query: {}, modelName: K): string; + /** + * Builds a URL for a `store.queryRecord(type, query)` call. + */ + urlForQueryRecord(query: {}, modelName: K): string; + /** + * Builds a URL for coalesceing multiple `store.findRecord(type, id)` + * records into 1 request when the adapter's `coalesceFindRequests` + * property is true. + */ + urlForFindMany( + ids: any[], + modelName: K, + snapshots: any[] + ): string; + /** + * Builds a URL for fetching a async hasMany relationship when a url + * is not provided by the server. + */ + urlForFindHasMany( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for fetching a async belongsTo relationship when a url + * is not provided by the server. + */ + urlForFindBelongsTo( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for a `record.save()` call when the record was created + * locally using `store.createRecord()`. + */ + urlForCreateRecord(modelName: K, snapshot: Snapshot): string; + /** + * Builds a URL for a `record.save()` call when the record has been update locally. + */ + urlForUpdateRecord( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for a `record.save()` call when the record has been deleted locally. + */ + urlForDeleteRecord( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Determines the pathname for a given type. + */ + pathForType(modelName: K): string; + } + /** + * A `DS.AdapterError` is used by an adapter to signal that an error occurred + * during a request to an external API. It indicates a generic error, and + * subclasses are used to indicate specific error states. The following + * subclasses are provided: + */ + class AdapterError extends Ember.Object {} + /** + * A `DS.InvalidError` is used by an adapter to signal the external API + * was unable to process a request because the content was not + * semantically correct or meaningful per the API. Usually this means a + * record failed some form of server side validation. When a promise + * from an adapter is rejected with a `DS.InvalidError` the record will + * transition to the `invalid` state and the errors will be set to the + * `errors` property on the record. + */ + class InvalidError extends AdapterError { + constructor(errors: any[]); + } + /** + * A `DS.TimeoutError` is used by an adapter to signal that a request + * to the external API has timed out. I.e. no response was received from + * the external API within an allowed time period. + */ + class TimeoutError extends AdapterError {} + /** + * A `DS.AbortError` is used by an adapter to signal that a request to + * the external API was aborted. For example, this can occur if the user + * navigates away from the current page after a request to the external API + * has been initiated but before a response has been received. + */ + class AbortError extends AdapterError {} + /** + * A `DS.UnauthorizedError` equates to a HTTP `401 Unauthorized` response + * status. It is used by an adapter to signal that a request to the external + * API was rejected because authorization is required and has failed or has not + * yet been provided. + */ + class UnauthorizedError extends AdapterError {} + /** + * A `DS.ForbiddenError` equates to a HTTP `403 Forbidden` response status. + * It is used by an adapter to signal that a request to the external API was + * valid but the server is refusing to respond to it. If authorization was + * provided and is valid, then the authenticated user does not have the + * necessary permissions for the request. + */ + class ForbiddenError extends AdapterError {} + /** + * A `DS.NotFoundError` equates to a HTTP `404 Not Found` response status. + * It is used by an adapter to signal that a request to the external API + * was rejected because the resource could not be found on the API. + */ + class NotFoundError extends AdapterError {} + /** + * A `DS.ConflictError` equates to a HTTP `409 Conflict` response status. + * It is used by an adapter to indicate that the request could not be processed + * because of a conflict in the request. An example scenario would be when + * creating a record with a client generated id but that id is already known + * to the external API. + */ + class ConflictError extends AdapterError {} + /** + * A `DS.ServerError` equates to a HTTP `500 Internal Server Error` response + * status. It is used by the adapter to indicate that a request has failed + * because of an error in the external API. + */ + class ServerError extends AdapterError {} + /** + * Holds validation errors for a given record, organized by attribute names. + */ + interface Errors extends Ember.Enumerable, Ember.Evented {} + class Errors extends Ember.Object { + /** + * DEPRECATED: + * Register with target handler + */ + registerHandlers( + target: {}, + becameInvalid: Function, + becameValid: Function + ): any; + /** + * Returns errors for a given attribute + */ + errorsFor(attribute: string): any[]; + /** + * An array containing all of the error messages for this + * record. This is useful for displaying all errors to the user. + */ + messages: Ember.ComputedProperty; + /** + * Total number of errors. + */ + length: Ember.ComputedProperty; + isEmpty: Ember.ComputedProperty; + /** + * DEPRECATED: + * Adds error messages to a given attribute and sends + * `becameInvalid` event to the record. + */ + add(attribute: string, messages: any[] | string): any; + /** + * DEPRECATED: + * Removes all error messages from the given attribute and sends + * `becameValid` event to the record if there no more errors left. + */ + remove(attribute: string): any; + /** + * DEPRECATED: + * Removes all error messages and sends `becameValid` event + * to the record. + */ + clear(): any; + /** + * Checks if there is error messages for the given attribute. + */ + has(attribute: string): boolean; + } + /** + * The model class that all Ember Data records descend from. + * This is the public API of Ember Data models. If you are using Ember Data + * in your application, this is the class you should use. + * If you are working on Ember Data internals, you most likely want to be dealing + * with `InternalModel` + */ + class Model extends Ember.Object { + /** + * If this property is `true` the record is in the `empty` + * state. Empty is the first state all records enter after they have + * been created. Most records created by the store will quickly + * transition to the `loading` state if data needs to be fetched from + * the server or the `created` state if the record is created on the + * client. A record can also enter the empty state if the adapter is + * unable to locate the record. + */ + isEmpty: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `loading` state. A + * record enters this state when the store asks the adapter for its + * data. It remains in this state until the adapter provides the + * requested data. + */ + isLoading: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `loaded` state. A + * record enters this state when its data is populated. Most of a + * record's lifecycle is spent inside substates of the `loaded` + * state. + */ + isLoaded: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `dirty` state. The + * record has local changes that have not yet been saved by the + * adapter. This includes records that have been created (but not yet + * saved) or deleted. + */ + hasDirtyAttributes: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `saving` state. A + * record enters the saving state when `save` is called, but the + * adapter has not yet acknowledged that the changes have been + * persisted to the backend. + */ + isSaving: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `deleted` state + * and has been marked for deletion. When `isDeleted` is true and + * `hasDirtyAttributes` is true, the record is deleted locally but the deletion + * was not yet persisted. When `isSaving` is true, the change is + * in-flight. When both `hasDirtyAttributes` and `isSaving` are false, the + * change has persisted. + */ + isDeleted: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `new` state. A + * record will be in the `new` state when it has been created on the + * client and the adapter has not yet report that it was successfully + * saved. + */ + isNew: Ember.ComputedProperty; + /** + * If this property is `true` the record is in the `valid` state. + */ + isValid: Ember.ComputedProperty; + /** + * If the record is in the dirty state this property will report what + * kind of change has caused it to move into the dirty + * state. Possible values are: + */ + dirtyType: Ember.ComputedProperty; + /** + * If `true` the adapter reported that it was unable to save local + * changes to the backend for any reason other than a server-side + * validation error. + */ + isError: boolean; + /** + * If `true` the store is attempting to reload the record from the adapter. + */ + isReloading: boolean; + /** + * All ember models have an id property. This is an identifier + * managed by an external source. These are always coerced to be + * strings before being used internally. Note when declaring the + * attributes for a model it is an error to declare an id + * attribute. + */ + id: string; + /** + * When the record is in the `invalid` state this object will contain + * any errors returned by the adapter. When present the errors hash + * contains keys corresponding to the invalid property names + * and values which are arrays of Javascript objects with two keys: + */ + errors: Ember.ComputedProperty; + /** + * This property holds the `DS.AdapterError` object with which + * last adapter operation was rejected. + */ + adapterError: AdapterError; + /** + * Create a JSON representation of the record, using the serialization + * strategy of the store's adapter. + */ + serialize(options?: { includeId?: boolean }): {}; + /** + * Use [DS.JSONSerializer](DS.JSONSerializer.html) to + * get the JSON representation of a record. + */ + toJSON(options: {}): {}; + /** + * Fired when the record is ready to be interacted with, + * that is either loaded from the server or created locally. + */ + ready(): void; + /** + * Fired when the record is loaded from the server. + */ + didLoad(): void; + /** + * Fired when the record is updated. + */ + didUpdate(): void; + /** + * Fired when a new record is commited to the server. + */ + didCreate(): void; + /** + * Fired when the record is deleted. + */ + didDelete(): void; + /** + * Fired when the record becomes invalid. + */ + becameInvalid(): void; + /** + * Fired when the record enters the error state. + */ + becameError(): void; + /** + * Fired when the record is rolled back. + */ + rolledBack(): void; + /** + * Marks the record as deleted but does not save it. You must call + * `save` afterwards if you want to persist it. You might use this + * method if you want to allow the user to still `rollbackAttributes()` + * after a delete was made. + */ + deleteRecord(): any; + /** + * Same as `deleteRecord`, but saves the record immediately. + */ + destroyRecord(options?: {}): RSVP.Promise; + /** + * Unloads the record from the store. This will cause the record to be destroyed and freed up for garbage collection. + */ + unloadRecord(): any; + /** + * Returns an object, whose keys are changed properties, and value is + * an [oldProp, newProp] array. + */ + changedAttributes(): ChangedAttributes; + /** + * If the model `hasDirtyAttributes` this function will discard any unsaved + * changes. If the model `isNew` it will be removed from the store. + */ + rollbackAttributes(): any; + /** + * Save the record and persist any changes to the record to an + * external source via the adapter. + */ + save(options?: {}): RSVP.Promise; + /** + * Reload the record from the adapter. + */ + reload(): RSVP.Promise; + /** + * Get the reference for the specified belongsTo relationship. + */ + belongsTo(name: RelationshipsFor): BelongsToReference; + /** + * Get the reference for the specified hasMany relationship. + */ + hasMany(name: RelationshipsFor): HasManyReference; + /** + * Given a callback, iterates over each of the relationships in the model, + * invoking the callback with the name of each relationship and its relationship + * descriptor. + */ + eachRelationship(callback: (name: string, details: RelationshipMeta) => void, binding?: any): any; + /** + * Represents the model's class name as a string. This can be used to look up the model's class name through + * `DS.Store`'s modelFor method. + */ + static modelName: keyof ModelRegistry; + /** + * For a given relationship name, returns the model type of the relationship. + */ + static typeForRelationship(name: K, store: Store): ModelRegistry[K]; + /** + * Find the relationship which is the inverse of the one asked for. + */ + static inverseFor(name: K, store: Store): {}; + /** + * The model's relationships as a map, keyed on the type of the + * relationship. The value of each entry is an array containing a descriptor + * for each relationship with that type, describing the name of the relationship + * as well as the type. + */ + static relationships: Ember.ComputedProperty; + /** + * A hash containing lists of the model's relationships, grouped + * by the relationship kind. For example, given a model with this + * definition: + */ + static relationshipNames: Ember.ComputedProperty<{}>; + /** + * An array of types directly related to a model. Each type will be + * included once, regardless of the number of relationships it has with + * the model. + */ + static relatedTypes: Ember.ComputedProperty< + Ember.NativeArray + >; + /** + * A map whose keys are the relationships of a model and whose values are + * relationship descriptors. + */ + static relationshipsByName: Ember.ComputedProperty; + /** + * A map whose keys are the fields of the model and whose values are strings + * describing the kind of the field. A model's fields are the union of all of its + * attributes and relationships. + */ + static fields: Ember.ComputedProperty; + /** + * Given a callback, iterates over each of the relationships in the model, + * invoking the callback with the name of each relationship and its relationship + * descriptor. + */ + static eachRelationship(callback: (name: string, details: RelationshipMeta) => void, binding?: any): any; + /** + * Given a callback, iterates over each of the types related to a model, + * invoking the callback with the related type's class. Each type will be + * returned just once, regardless of how many different relationships it has + * with a model. + */ + static eachRelatedType(callback: Function, binding: any): any; + /** + * A map whose keys are the attributes of the model (properties + * described by DS.attr) and whose values are the meta object for the + * property. + */ + static attributes: Ember.ComputedProperty; + /** + * A map whose keys are the attributes of the model (properties + * described by DS.attr) and whose values are type of transformation + * applied to each attribute. This map does not include any + * attributes that do not have an transformation type. + */ + static transformedAttributes: Ember.ComputedProperty; + /** + * Iterates through the attributes of the model, calling the passed function on each + * attribute. + */ + static eachAttribute(callback: Function, binding: {}): any; + /** + * Iterates through the transformedAttributes of the model, calling + * the passed function on each attribute. Note the callback will not be + * called for any attributes that do not have an transformation type. + */ + static eachTransformedAttribute( + callback: Function, + binding: {} + ): any; + /** + * Discards any unsaved changes to the given attribute. This feature is not enabled by default. You must enable `ds-rollback-attribute` and be running a canary build. + */ + rollbackAttribute(): any; + /** + * This Ember.js hook allows an object to be notified when a property + * is defined. + */ + didDefineProperty( + proto: {}, + key: string, + value: Ember.ComputedProperty + ): any; + } + /** + * ### State + */ + class RootState {} + /** + * Represents an ordered list of records whose order and membership is + * determined by the adapter. For example, a query sent to the adapter + * may trigger a search on the server, whose results would be loaded + * into an instance of the `AdapterPopulatedRecordArray`. + */ + class AdapterPopulatedRecordArray extends RecordArray {} + /** + * Represents a list of records whose membership is determined by the + * store. As records are created, loaded, or modified, the store + * evaluates them to determine if they should be part of the record + * array. + */ + class FilteredRecordArray extends RecordArray { + /** + * The filterFunction is a function used to test records from the store to + * determine if they should be part of the record array. + */ + filterFunction(record: Model): boolean; + } + /** + * A record array is an array that contains records of a certain modelName. The record + * array materializes records as needed when they are retrieved for the first + * time. You should not create record arrays yourself. Instead, an instance of + * `DS.RecordArray` or its subclasses will be returned by your application's store + * in response to queries. + */ + interface RecordArray extends Ember.ArrayProxy, Ember.Evented {} + class RecordArray { + /** + * The flag to signal a `RecordArray` is finished loading data. + */ + isLoaded: boolean; + /** + * The flag to signal a `RecordArray` is currently loading data. + */ + isUpdating: boolean; + /** + * The modelClass represented by this record array. + */ + type: Ember.ComputedProperty; + /** + * Used to get the latest version of all of the records in this array + * from the adapter. + */ + update(): any; + /** + * Saves all of the records in the `RecordArray`. + */ + save(): PromiseArray; + } + /** + * A BelongsToReference is a low level API that allows users and + * addon author to perform meta-operations on a belongs-to + * relationship. + */ + class BelongsToReference { + /** + * This returns a string that represents how the reference will be + * looked up when it is loaded. If the relationship has a link it will + * use the "link" otherwise it defaults to "id". + */ + remoteType(): string; + /** + * The `id` of the record that this reference refers to. Together, the + * `type()` and `id()` methods form a composite key for the identity + * map. This can be used to access the id of an async relationship + * without triggering a fetch that would normally happen if you + * attempted to use `record.get('relationship.id')`. + */ + id(): string; + /** + * The link Ember Data will use to fetch or reload this belongs-to + * relationship. + */ + link(): string; + /** + * The meta data for the belongs-to relationship. + */ + meta(): {}; + /** + * `push` can be used to update the data in the relationship and Ember + * Data will treat the new data as the conanical value of this + * relationship on the backend. + */ + push(objectOrPromise: {} | RSVP.Promise): RSVP.Promise; + /** + * `value()` synchronously returns the current value of the belongs-to + * relationship. Unlike `record.get('relationshipName')`, calling + * `value()` on a reference does not trigger a fetch if the async + * relationship is not yet loaded. If the relationship is not loaded + * it will always return `null`. + */ + value(objectOrPromise: {} | RSVP.Promise): Model; + /** + * Loads a record in a belongs to relationship if it is not already + * loaded. If the relationship is already loaded this method does not + * trigger a new load. + */ + load(): RSVP.Promise; + /** + * Triggers a reload of the value in this relationship. If the + * remoteType is `"link"` Ember Data will use the relationship link to + * reload the relationship. Otherwise it will reload the record by its + * id. + */ + reload(): RSVP.Promise; + } + /** + * A HasManyReference is a low level API that allows users and addon + * author to perform meta-operations on a has-many relationship. + */ + class HasManyReference { + /** + * This returns a string that represents how the reference will be + * looked up when it is loaded. If the relationship has a link it will + * use the "link" otherwise it defaults to "id". + */ + remoteType(): string; + /** + * The link Ember Data will use to fetch or reload this has-many + * relationship. + */ + link(): string; + /** + * `ids()` returns an array of the record ids in this relationship. + */ + ids(): any[]; + /** + * The meta data for the has-many relationship. + */ + meta(): {}; + /** + * `push` can be used to update the data in the relationship and Ember + * Data will treat the new data as the canonical value of this + * relationship on the backend. + */ + push(objectOrPromise: T[] | RSVP.Promise): ManyArray; + /** + * `value()` synchronously returns the current value of the has-many + * relationship. Unlike `record.get('relationshipName')`, calling + * `value()` on a reference does not trigger a fetch if the async + * relationship is not yet loaded. If the relationship is not loaded + * it will always return `null`. + */ + value(): ManyArray; + /** + * Loads the relationship if it is not already loaded. If the + * relationship is already loaded this method does not trigger a new + * load. + */ + load(): RSVP.Promise; + /** + * Reloads this has-many relationship. + */ + reload(): RSVP.Promise; + } + /** + * An RecordReference is a low level API that allows users and + * addon author to perform meta-operations on a record. + */ + class RecordReference { + /** + * The `id` of the record that this reference refers to. + */ + id(): string; + /** + * How the reference will be looked up when it is loaded: Currently + * this always return `identity` to signifying that a record will be + * loaded by the `type` and `id`. + */ + remoteType(): string; + /** + * This API allows you to provide a reference with new data. The + * simplest usage of this API is similar to `store.push`: you provide a + * normalized hash of data and the object represented by the reference + * will update. + */ + push(payload: RSVP.Promise | {}): PromiseObject & T; + /** + * If the entity referred to by the reference is already loaded, it is + * present as `reference.value`. Otherwise the value returned by this function + * is `null`. + */ + value(): T | null; + /** + * Triggers a fetch for the backing entity based on its `remoteType` + * (see `remoteType` definitions per reference type). + */ + load(): PromiseObject & T; + /** + * Reloads the record if it is already loaded. If the record is not + * loaded it will load the record via `store.findRecord` + */ + reload(): PromiseObject & T; + } + /** + * A `ManyArray` is a `MutableArray` that represents the contents of a has-many + * relationship. + */ + interface ManyArray extends Ember.MutableArray {} + class ManyArray extends Ember.Object.extend( + Ember.MutableArray as {}, + Ember.Evented + ) { + /** + * The loading state of this array + */ + isLoaded: boolean; + /** + * Metadata associated with the request for async hasMany relationships. + */ + meta: {}; + /** + * Reloads all of the records in the manyArray. If the manyArray + * holds a relationship that was originally fetched using a links url + * Ember Data will revisit the original links url to repopulate the + * relationship. + */ + reload(): PromiseArray; + /** + * Saves all of the records in the `ManyArray`. + */ + save(): PromiseArray; + /** + * Create a child record within the owner + */ + createRecord(inputProperties?: {}): T; + } + /** + * A `PromiseArray` is an object that acts like both an `Ember.Array` + * and a promise. When the promise is resolved the resulting value + * will be set to the `PromiseArray`'s `content` property. This makes + * it easy to create data bindings with the `PromiseArray` that will be + * updated when the promise resolves. + */ + interface PromiseArray + extends Ember.ArrayProxy, + Ember.PromiseProxyMixin> {} + class PromiseArray {} + /** + * A `PromiseObject` is an object that acts like both an `Ember.Object` + * and a promise. When the promise is resolved, then the resulting value + * will be set to the `PromiseObject`'s `content` property. This makes + * it easy to create data bindings with the `PromiseObject` that will + * be updated when the promise resolves. + */ + interface PromiseObject + extends Ember.ObjectProxy, + Ember.PromiseProxyMixin {} + class PromiseObject {} + /** + * A PromiseManyArray is a PromiseArray that also proxies certain method calls + * to the underlying manyArray. + * Right now we proxy: + */ + class PromiseManyArray extends PromiseArray { + /** + * Reloads all of the records in the manyArray. If the manyArray + * holds a relationship that was originally fetched using a links url + * Ember Data will revisit the original links url to repopulate the + * relationship. + */ + reload(): PromiseManyArray; + /** + * Create a child record within the owner + */ + createRecord(inputProperties?: {}): T; + } + class SnapshotRecordArray { + /** + * Number of records in the array + */ + length: number; + /** + * Meta objects for the record array. + */ + meta: {}; + /** + * A hash of adapter options passed into the store method for this request. + */ + adapterOptions: {}; + /** + * The relationships to include for this request. + */ + include: string | any[]; + /** + * The type of the underlying records for the snapshots in the array, as a DS.Model + */ + type: ModelRegistry[K]; + /** + * Get snapshots of the underlying record array + */ + snapshots(): any[]; + } + class Snapshot { + /** + * The underlying record for this snapshot. Can be used to access methods and + * properties defined on the record. + */ + record: ModelRegistry[K]; + /** + * The id of the snapshot's underlying record + */ + id: string; + /** + * A hash of adapter options + */ + adapterOptions: {}; + /** + * The name of the type of the underlying record for this snapshot, as a string. + */ + modelName: K; + /** + * The type of the underlying record for this snapshot, as a DS.Model. + */ + type: ModelRegistry[K]; + /** + * Returns the value of an attribute. + */ + attr>(keyName: L): ModelRegistry[K][L]; + /** + * Returns all attributes and their corresponding values. + */ + attributes(): { [L in keyof ModelRegistry[K]]: ModelRegistry[K][L] }; + /** + * Returns all changed attributes and their old and new values. + */ + changedAttributes(): Partial<{ [L in keyof ModelRegistry[K]]: ModelRegistry[K][L] }>; + /** + * Returns the current value of a belongsTo relationship. + */ + belongsTo>( + keyName: L, + options?: {} + ): Snapshot['record'][L] | string | null | undefined; + /** + * Returns the current value of a hasMany relationship. + */ + hasMany>( + keyName: L, + options?: { ids: false } + ): Array['record'][L]> | undefined; + hasMany>( + keyName: L, + options: { ids: true } + ): string[] | undefined; + /** + * Iterates through all the attributes of the model, calling the passed + * function on each attribute. + */ + eachAttribute(callback: (key: keyof M, meta: AttributeMeta) => void, binding?: {}): any; + /** + * Iterates through all the relationships of the model, calling the passed + * function on each relationship. + */ + eachRelationship(callback: (key: keyof M, meta: RelationshipMeta) => void, binding?: {}): any; + /** + * Serializes the snapshot using the serializer for the model. + */ + serialize(options: {}): {}; + } + + /** + * The store contains all of the data for records loaded from the server. + * It is also responsible for creating instances of `DS.Model` that wrap + * the individual data for a record, so that they can be bound to in your + * Handlebars templates. + */ + class Store { + /** + * The default adapter to use to communicate to a backend server or + * other persistence layer. This will be overridden by an application + * adapter if present. + */ + adapter: string; + /** + * Create a new record in the current store. The properties passed + * to this method are set on the newly created record. + */ + createRecord( + modelName: K, + inputProperties?: {} + ): ModelRegistry[K]; + /** + * For symmetry, a record can be deleted via the store. + */ + deleteRecord(record: Model): void; + /** + * For symmetry, a record can be unloaded via the store. + * This will cause the record to be destroyed and freed up for garbage collection. + */ + unloadRecord(record: Model): void; + /** + * This method returns a record for a given type and id combination. + */ + findRecord( + modelName: K, + id: string | number, + options?: {} + ): PromiseObject & ModelRegistry[K]; + /** + * Get the reference for the specified record. + */ + getReference( + modelName: K, + id: string | number + ): RecordReference; + /** + * Get a record by a given type and ID without triggering a fetch. + */ + peekRecord( + modelName: K, + id: string | number + ): ModelRegistry[K] | null; + /** + * This method returns true if a record for a given modelName and id is already + * loaded in the store. Use this function to know beforehand if a findRecord() + * will result in a request or that it will be a cache hit. + */ + hasRecordForId( + modelName: K, + id: string | number + ): boolean; + /** + * This method delegates a query to the adapter. This is the one place where + * adapter-level semantics are exposed to the application. + */ + query( + modelName: K, + query: any + ): AdapterPopulatedRecordArray & PromiseArray; + /** + * This method makes a request for one record, where the `id` is not known + * beforehand (if the `id` is known, use [`findRecord`](#method_findRecord) + * instead). + */ + queryRecord( + modelName: K, + query: any + ): RSVP.Promise; + /** + * `findAll` asks the adapter's `findAll` method to find the records for the + * given type, and returns a promise which will resolve with all records of + * this type present in the store, even if the adapter only returns a subset + * of them. + */ + findAll( + modelName: K, + options?: { + reload?: boolean; + backgroundReload?: boolean; + include?: string; + adapterOptions?: any; + } + ): PromiseArray; + /** + * This method returns a filtered array that contains all of the + * known records for a given type in the store. + */ + peekAll(modelName: K): RecordArray; + /** + * This method unloads all records in the store. + * It schedules unloading to happen during the next run loop. + */ + unloadAll(modelName: K): void; + /** + * DEPRECATED: + * This method has been deprecated and is an alias for store.hasRecordForId, which should + * be used instead. + */ + recordIsLoaded(modelName: K, id: string): boolean; + /** + * Returns the model class for the particular `modelName`. + */ + modelFor(modelName: K): ModelRegistry[K]; + /** + * Push some data for a given type into the store. + */ + push(data: {}): Model | any[]; + /** + * Push some raw data into the store. + */ + pushPayload(modelName: K, inputPayload: {}): any; + pushPayload(inputPayload: {}): any; + /** + * `normalize` converts a json payload into the normalized form that + * [push](#method_push) expects. + */ + normalize(modelName: K, payload: {}): {}; + /** + * Returns an instance of the adapter for a given type. For + * example, `adapterFor('person')` will return an instance of + * `App.PersonAdapter`. + */ + adapterFor(modelName: K): AdapterRegistry[K]; + /** + * Returns an instance of the serializer for a given type. For + * example, `serializerFor('person')` will return an instance of + * `App.PersonSerializer`. + */ + serializerFor(modelName: K): SerializerRegistry[K]; + } + /** + * The `JSONAPIAdapter` is the default adapter used by Ember Data. It + * is responsible for transforming the store's requests into HTTP + * requests that follow the [JSON API](http://jsonapi.org/format/) + * format. + */ + class JSONAPIAdapter extends RESTAdapter { + /** + * By default the JSONAPIAdapter will send each find request coming from a `store.find` + * or from accessing a relationship separately to the server. If your server supports passing + * ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests + * within a single runloop. + */ + coalesceFindRequests: boolean; + } + /** + * The REST adapter allows your store to communicate with an HTTP server by + * transmitting JSON via XHR. Most Ember.js apps that consume a JSON API + * should use the REST adapter. + */ + class RESTAdapter extends Adapter implements BuildURLMixin { + /** + * Takes a URL, an HTTP method and a hash of data, and makes an HTTP request. + */ + ajax( + url: string, + type: string, + options?: object + ): RSVP.Promise; + /** + * Generate ajax options + */ + ajaxOptions(url: string, type: string, options?: object): object; + /** + * By default, the RESTAdapter will send the query params sorted alphabetically to the + * server. + */ + sortQueryParams(obj: {}): {}; + /** + * By default the RESTAdapter will send each find request coming from a `store.find` + * or from accessing a relationship separately to the server. If your server supports passing + * ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests + * within a single runloop. + */ + coalesceFindRequests: boolean; + /** + * Endpoint paths can be prefixed with a `namespace` by setting the namespace + * property on the adapter: + */ + namespace: string; + /** + * An adapter can target other hosts by setting the `host` property. + */ + host: string; + /** + * Some APIs require HTTP headers, e.g. to provide an API + * key. Arbitrary headers can be set as key/value pairs on the + * `RESTAdapter`'s `headers` object and Ember Data will send them + * along with each ajax request. For dynamic headers see [headers + * customization](/api/data/classes/DS.RESTAdapter.html#toc_headers-customization). + */ + headers: {}; + /** + * Called by the store in order to fetch the JSON for a given + * type and ID. + */ + findRecord( + store: Store, + type: ModelRegistry[K], + id: string, + snapshot: Snapshot + ): RSVP.Promise; + /** + * Called by the store in order to fetch a JSON array for all + * of the records for a given type. + */ + findAll( + store: Store, + type: ModelRegistry[K], + sinceToken: string, + snapshotRecordArray: SnapshotRecordArray + ): RSVP.Promise; + /** + * Called by the store in order to fetch a JSON array for + * the records that match a particular query. + */ + query(store: Store, type: ModelRegistry[K], query: {}): RSVP.Promise; + /** + * Called by the store in order to fetch a JSON object for + * the record that matches a particular query. + */ + queryRecord( + store: Store, + type: ModelRegistry[K], + query: {} + ): RSVP.Promise; + /** + * Called by the store in order to fetch several records together if `coalesceFindRequests` is true + */ + findMany( + store: Store, + type: ModelRegistry[K], + ids: any[], + snapshots: any[] + ): RSVP.Promise; + /** + * Called by the store in order to fetch a JSON array for + * the unloaded records in a has-many relationship that were originally + * specified as a URL (inside of `links`). + */ + findHasMany( + store: Store, + snapshot: Snapshot, + url: string, + relationship: {} + ): RSVP.Promise; + /** + * Called by the store in order to fetch the JSON for the unloaded record in a + * belongs-to relationship that was originally specified as a URL (inside of + * `links`). + */ + findBelongsTo( + store: Store, + snapshot: Snapshot, + url: string + ): RSVP.Promise; + /** + * Called by the store when a newly created record is + * saved via the `save` method on a model record instance. + */ + createRecord( + store: Store, + type: ModelRegistry[K], + snapshot: Snapshot + ): RSVP.Promise; + /** + * Called by the store when an existing record is saved + * via the `save` method on a model record instance. + */ + updateRecord( + store: Store, + type: ModelRegistry[K], + snapshot: Snapshot + ): RSVP.Promise; + /** + * Called by the store when a record is deleted. + */ + deleteRecord( + store: Store, + type: ModelRegistry[K], + snapshot: Snapshot + ): RSVP.Promise; + /** + * Organize records into groups, each of which is to be passed to separate + * calls to `findMany`. + */ + groupRecordsForFindMany(store: Store, snapshots: any[]): any[]; + /** + * Takes an ajax response, and returns the json payload or an error. + */ + handleResponse( + status: number, + headers: {}, + payload: {}, + requestData: {} + ): {}; + /** + * Default `handleResponse` implementation uses this hook to decide if the + * response is a success. + */ + isSuccess(status: number, headers: {}, payload: {}): boolean; + /** + * Default `handleResponse` implementation uses this hook to decide if the + * response is an invalid error. + */ + isInvalid(status: number, headers: {}, payload: {}): boolean; + /** + * Get the data (body or query params) for a request. + */ + dataForRequest(params: {}): {}; + /** + * Get the HTTP method for a request. + */ + methodForRequest(params: {}): string; + /** + * Get the URL for a request. + */ + urlForRequest(params: {}): string; + /** + * Get the headers for a request. + */ + headersForRequest(params: {}): {}; + /** + * Builds a URL for a given type and optional ID. + */ + buildURL( + modelName?: K, + id?: string | any[] | {} | null, + snapshot?: Snapshot | any[] | null, + requestType?: string, + query?: {} + ): string; + /** + * Builds a URL for a `store.findRecord(type, id)` call. + */ + urlForFindRecord( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for a `store.findAll(type)` call. + */ + urlForFindAll( + modelName: K, + snapshot: SnapshotRecordArray + ): string; + /** + * Builds a URL for a `store.query(type, query)` call. + */ + urlForQuery(query: {}, modelName: K): string; + /** + * Builds a URL for a `store.queryRecord(type, query)` call. + */ + urlForQueryRecord(query: {}, modelName: K): string; + /** + * Builds a URL for coalesceing multiple `store.findRecord(type, id)` + * records into 1 request when the adapter's `coalesceFindRequests` + * property is true. + */ + urlForFindMany( + ids: any[], + modelName: K, + snapshots: any[] + ): string; + /** + * Builds a URL for fetching a async hasMany relationship when a url + * is not provided by the server. + */ + urlForFindHasMany( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for fetching a async belongsTo relationship when a url + * is not provided by the server. + */ + urlForFindBelongsTo( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for a `record.save()` call when the record was created + * locally using `store.createRecord()`. + */ + urlForCreateRecord(modelName: K, snapshot: Snapshot): string; + /** + * Builds a URL for a `record.save()` call when the record has been update locally. + */ + urlForUpdateRecord( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Builds a URL for a `record.save()` call when the record has been deleted locally. + */ + urlForDeleteRecord( + id: string, + modelName: K, + snapshot: Snapshot + ): string; + /** + * Determines the pathname for a given type. + */ + pathForType(modelName: K): string; + } + /** + * ## Using Embedded Records + */ + class EmbeddedRecordsMixin { + /** + * Normalize the record and recursively normalize/extract all the embedded records + * while pushing them into the store as they are encountered + */ + normalize(typeClass: Model, hash: {}, prop: string): {}; + /** + * Serialize `belongsTo` relationship when it is configured as an embedded object. + */ + serializeBelongsTo( + snapshot: Snapshot, + json: {}, + relationship: {} + ): any; + /** + * Serializes `hasMany` relationships when it is configured as embedded objects. + */ + serializeHasMany( + snapshot: Snapshot, + json: {}, + relationship: {} + ): any; + /** + * When serializing an embedded record, modify the property (in the json payload) + * that refers to the parent record (foreign key for relationship). + */ + removeEmbeddedForeignKey( + snapshot: Snapshot, + embeddedSnapshot: Snapshot, + relationship: {}, + json: {} + ): any; + } + /** + * Ember Data 2.0 Serializer: + */ + class JSONAPISerializer extends JSONSerializer { + pushPayload(store: Store, payload: {}): any; + /** + * Dasherizes and singularizes the model name in the payload to match + * the format Ember Data uses internally for the model name. + */ + modelNameFromPayloadKey(key: string): string; + /** + * Converts the model name to a pluralized version of the model name. + */ + payloadKeyFromModelName(modelName: K): string; + /** + * `keyForAttribute` can be used to define rules for how to convert an + * attribute name in your model to a key in your JSON. + * By default `JSONAPISerializer` follows the format used on the examples of + * http://jsonapi.org/format and uses dashes as the word separator in the JSON + * attribute keys. + */ + keyForAttribute(key: string, method: string): string; + /** + * `keyForRelationship` can be used to define a custom key when + * serializing and deserializing relationship properties. + * By default `JSONAPISerializer` follows the format used on the examples of + * http://jsonapi.org/format and uses dashes as word separators in + * relationship properties. + */ + keyForRelationship( + key: string, + typeClass: string, + method: string + ): string; + /** + * `modelNameFromPayloadType` can be used to change the mapping for a DS model + * name, taken from the value in the payload. + */ + modelNameFromPayloadType(payloadType: string): string; + /** + * `payloadTypeFromModelName` can be used to change the mapping for the type in + * the payload, taken from the model name. + */ + payloadTypeFromModelName(modelName: K): string; + } + /** + * Ember Data 2.0 Serializer: + */ + class JSONSerializer extends Serializer { + /** + * The `primaryKey` is used when serializing and deserializing + * data. Ember Data always uses the `id` property to store the id of + * the record. The external source may not always follow this + * convention. In these cases it is useful to override the + * `primaryKey` property to match the `primaryKey` of your external + * store. + */ + primaryKey: string; + /** + * The `attrs` object can be used to declare a simple mapping between + * property names on `DS.Model` records and payload keys in the + * serialized JSON object representing the record. An object with the + * property `key` can also be used to designate the attribute's key on + * the response payload. + */ + attrs: {}; + /** + * The `normalizeResponse` method is used to normalize a payload from the + * server to a JSON-API Document. + */ + normalizeResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeFindRecordResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeQueryRecordResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeFindAllResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeFindBelongsToResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeFindHasManyResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeFindManyResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeQueryResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeCreateRecordResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeDeleteRecordResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeUpdateRecordResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeSaveResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeSingleResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + normalizeArrayResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + /** + * Normalizes a part of the JSON payload returned by + * the server. You should override this method, munge the hash + * and call super if you have generic normalization to do. + */ + normalize(typeClass: Model, hash: {}): {}; + /** + * Returns the resource's ID. + */ + extractId(modelClass: {}, resourceHash: {}): string; + /** + * Returns the resource's attributes formatted as a JSON-API "attributes object". + */ + extractAttributes(modelClass: {}, resourceHash: {}): {}; + /** + * Returns a relationship formatted as a JSON-API "relationship object". + */ + extractRelationship( + relationshipModelName: {}, + relationshipHash: {} + ): {}; + /** + * Returns a polymorphic relationship formatted as a JSON-API "relationship object". + */ + extractPolymorphicRelationship( + relationshipModelName: {}, + relationshipHash: {}, + relationshipOptions: {} + ): {}; + /** + * Returns the resource's relationships formatted as a JSON-API "relationships object". + */ + extractRelationships(modelClass: {}, resourceHash: {}): {}; + modelNameFromPayloadKey(key: string): string; + /** + * Check if the given hasMany relationship should be serialized + */ + shouldSerializeHasMany( + snapshot: Snapshot, + key: string, + relationshipType: string + ): boolean; + /** + * Called when a record is saved in order to convert the + * record into JSON. + */ + serialize(snapshot: Snapshot, options: {}): {}; + /** + * You can use this method to customize how a serialized record is added to the complete + * JSON hash to be sent to the server. By default the JSON Serializer does not namespace + * the payload and just sends the raw serialized JSON object. + * If your server expects namespaced keys, you should consider using the RESTSerializer. + * Otherwise you can override this method to customize how the record is added to the hash. + * The hash property should be modified by reference. + */ + serializeIntoHash( + hash: {}, + typeClass: ModelRegistry[K], + snapshot: Snapshot, + options?: {} + ): any; + /** + * `serializeAttribute` can be used to customize how `DS.attr` + * properties are serialized + */ + serializeAttribute( + snapshot: Snapshot, + json: {}, + key: string, + attribute: {} + ): any; + /** + * `serializeBelongsTo` can be used to customize how `DS.belongsTo` + * properties are serialized. + */ + serializeBelongsTo( + snapshot: Snapshot, + json: {}, + relationship: {} + ): any; + /** + * `serializeHasMany` can be used to customize how `DS.hasMany` + * properties are serialized. + */ + serializeHasMany( + snapshot: Snapshot, + json: {}, + relationship: {} + ): any; + /** + * You can use this method to customize how polymorphic objects are + * serialized. Objects are considered to be polymorphic if + * `{ polymorphic: true }` is pass as the second argument to the + * `DS.belongsTo` function. + */ + serializePolymorphicType( + snapshot: Snapshot, + json: {}, + relationship: {} + ): any; + /** + * `extractMeta` is used to deserialize any meta information in the + * adapter payload. By default Ember Data expects meta information to + * be located on the `meta` property of the payload object. + */ + extractMeta(store: Store, modelClass: Model, payload: {}): any; + /** + * `extractErrors` is used to extract model errors when a call + * to `DS.Model#save` fails with an `InvalidError`. By default + * Ember Data expects error information to be located on the `errors` + * property of the payload object. + */ + extractErrors( + store: Store, + typeClass: Model, + payload: {}, + id: string | number + ): {}; + /** + * `keyForAttribute` can be used to define rules for how to convert an + * attribute name in your model to a key in your JSON. + */ + keyForAttribute(key: string, method: string): string; + /** + * `keyForRelationship` can be used to define a custom key when + * serializing and deserializing relationship properties. By default + * `JSONSerializer` does not provide an implementation of this method. + */ + keyForRelationship( + key: string, + typeClass: string, + method: string + ): string; + /** + * `keyForLink` can be used to define a custom key when deserializing link + * properties. + */ + keyForLink(key: string, kind: string): string; + modelNameFromPayloadType(type: string): string; + /** + * serializeId can be used to customize how id is serialized + * For example, your server may expect integer datatype of id + */ + serializeId(snapshot: Snapshot, json: {}, primaryKey: string): any; + } + /** + * Normally, applications will use the `RESTSerializer` by implementing + * the `normalize` method. + */ + class RESTSerializer extends JSONSerializer { + /** + * `keyForPolymorphicType` can be used to define a custom key when + * serializing and deserializing a polymorphic type. By default, the + * returned key is `${key}Type`. + */ + keyForPolymorphicType( + key: string, + typeClass: string, + method: string + ): string; + /** + * Normalizes a part of the JSON payload returned by + * the server. You should override this method, munge the hash + * and call super if you have generic normalization to do. + */ + normalize(modelClass: Model, resourceHash: {}, prop?: string): {}; + /** + * This method allows you to push a payload containing top-level + * collections of records organized per type. + */ + pushPayload(store: Store, payload: {}): any; + /** + * This method is used to convert each JSON root key in the payload + * into a modelName that it can use to look up the appropriate model for + * that part of the payload. + */ + modelNameFromPayloadKey(key: string): string; + /** + * Called when a record is saved in order to convert the + * record into JSON. + */ + serialize(snapshot: Snapshot, options: {}): {}; + /** + * You can use this method to customize the root keys serialized into the JSON. + * The hash property should be modified by reference (possibly using something like _.extend) + * By default the REST Serializer sends the modelName of a model, which is a camelized + * version of the name. + */ + serializeIntoHash( + hash: {}, + typeClass: Model, + snapshot: Snapshot, + options?: {} + ): any; + /** + * You can use `payloadKeyFromModelName` to override the root key for an outgoing + * request. By default, the RESTSerializer returns a camelized version of the + * model's name. + */ + payloadKeyFromModelName(modelName: K): string; + /** + * You can use this method to customize how polymorphic objects are serialized. + * By default the REST Serializer creates the key by appending `Type` to + * the attribute and value from the model's camelcased model name. + */ + serializePolymorphicType( + snapshot: Snapshot, + json: {}, + relationship: {} + ): any; + /** + * You can use this method to customize how a polymorphic relationship should + * be extracted. + */ + extractPolymorphicRelationship( + relationshipType: {}, + relationshipHash: {}, + relationshipOptions: {} + ): {}; + /** + * `modelNameFromPayloadType` can be used to change the mapping for a DS model + * name, taken from the value in the payload. + */ + modelNameFromPayloadType(payloadType: string): string; + /** + * `payloadTypeFromModelName` can be used to change the mapping for the type in + * the payload, taken from the model name. + */ + payloadTypeFromModelName(modelName: K): string; + } + /** + * The `DS.BooleanTransform` class is used to serialize and deserialize + * boolean attributes on Ember Data record objects. This transform is + * used when `boolean` is passed as the type parameter to the + * [DS.attr](../../data#method_attr) function. + */ + class BooleanTransform extends Transform {} + /** + * The `DS.DateTransform` class is used to serialize and deserialize + * date attributes on Ember Data record objects. This transform is used + * when `date` is passed as the type parameter to the + * [DS.attr](../../data#method_attr) function. It uses the [`ISO 8601`](https://en.wikipedia.org/wiki/ISO_8601) + * standard. + */ + class DateTransform extends Transform {} + /** + * The `DS.NumberTransform` class is used to serialize and deserialize + * numeric attributes on Ember Data record objects. This transform is + * used when `number` is passed as the type parameter to the + * [DS.attr](../../data#method_attr) function. + */ + class NumberTransform extends Transform {} + /** + * The `DS.StringTransform` class is used to serialize and deserialize + * string attributes on Ember Data record objects. This transform is + * used when `string` is passed as the type parameter to the + * [DS.attr](../../data#method_attr) function. + */ + class StringTransform extends Transform {} + /** + * The `DS.Transform` class is used to serialize and deserialize model + * attributes when they are saved or loaded from an + * adapter. Subclassing `DS.Transform` is useful for creating custom + * attributes. All subclasses of `DS.Transform` must implement a + * `serialize` and a `deserialize` method. + */ + class Transform extends Ember.Object { + /** + * When given a deserialized value from a record attribute this + * method must return the serialized value. + */ + serialize(deserialized: any, options: AttrOptions): any; + /** + * When given a serialize value from a JSON object this method must + * return the deserialized value for the record attribute. + */ + deserialize(serialized: any, options: AttrOptions): any; + } + /** + * An adapter is an object that receives requests from a store and + * translates them into the appropriate action to take against your + * persistence layer. The persistence layer is usually an HTTP API, but + * may be anything, such as the browser's local storage. Typically the + * adapter is not invoked directly instead its functionality is accessed + * through the `store`. + */ + class Adapter extends Ember.Object { + /** + * If you would like your adapter to use a custom serializer you can + * set the `defaultSerializer` property to be the name of the custom + * serializer. + */ + defaultSerializer: string; + /** + * The `findRecord()` method is invoked when the store is asked for a record that + * has not previously been loaded. In response to `findRecord()` being called, you + * should query your persistence layer for a record with the given ID. The `findRecord` + * method should return a promise that will resolve to a JavaScript object that will be + * normalized by the serializer. + */ + findRecord( + store: Store, + type: ModelRegistry[K], + id: string, + snapshot: Snapshot + ): RSVP.Promise; + /** + * The `findAll()` method is used to retrieve all records for a given type. + */ + findAll( + store: Store, + type: ModelRegistry[K], + sinceToken: string, + snapshotRecordArray: SnapshotRecordArray + ): RSVP.Promise; + /** + * This method is called when you call `query` on the store. + */ + query( + store: Store, + type: ModelRegistry[K], + query: {}, + recordArray: AdapterPopulatedRecordArray + ): RSVP.Promise; + /** + * The `queryRecord()` method is invoked when the store is asked for a single + * record through a query object. + */ + queryRecord( + store: Store, + type: ModelRegistry[K], + query: {} + ): RSVP.Promise; + /** + * If the globally unique IDs for your records should be generated on the client, + * implement the `generateIdForRecord()` method. This method will be invoked + * each time you create a new record, and the value returned from it will be + * assigned to the record's `primaryKey`. + */ + generateIdForRecord( + store: Store, + type: ModelRegistry[K], + inputProperties: {} + ): string | number; + /** + * Proxies to the serializer's `serialize` method. + */ + serialize(snapshot: Snapshot, options: {}): {}; + /** + * Implement this method in a subclass to handle the creation of + * new records. + */ + createRecord( + store: Store, + type: ModelRegistry[K], + snapshot: Snapshot + ): RSVP.Promise; + /** + * Implement this method in a subclass to handle the updating of + * a record. + */ + updateRecord( + store: Store, + type: ModelRegistry[K], + snapshot: Snapshot + ): RSVP.Promise; + /** + * Implement this method in a subclass to handle the deletion of + * a record. + */ + deleteRecord( + store: Store, + type: ModelRegistry[K], + snapshot: Snapshot + ): RSVP.Promise; + /** + * By default the store will try to coalesce all `fetchRecord` calls within the same runloop + * into as few requests as possible by calling groupRecordsForFindMany and passing it into a findMany call. + * You can opt out of this behaviour by either not implementing the findMany hook or by setting + * coalesceFindRequests to false. + */ + coalesceFindRequests: boolean; + /** + * The store will call `findMany` instead of multiple `findRecord` + * requests to find multiple records at once if coalesceFindRequests + * is true. + */ + findMany( + store: Store, + type: ModelRegistry[K], + ids: any[], + snapshots: any[] + ): RSVP.Promise; + /** + * Organize records into groups, each of which is to be passed to separate + * calls to `findMany`. + */ + groupRecordsForFindMany(store: Store, snapshots: any[]): any[]; + /** + * This method is used by the store to determine if the store should + * reload a record from the adapter when a record is requested by + * `store.findRecord`. + */ + shouldReloadRecord(store: Store, snapshot: Snapshot): boolean; + /** + * This method is used by the store to determine if the store should + * reload all records from the adapter when records are requested by + * `store.findAll`. + */ + shouldReloadAll( + store: Store, + snapshotRecordArray: SnapshotRecordArray + ): boolean; + /** + * This method is used by the store to determine if the store should + * reload a record after the `store.findRecord` method resolves a + * cached record. + */ + shouldBackgroundReloadRecord( + store: Store, + snapshot: Snapshot + ): boolean; + /** + * This method is used by the store to determine if the store should + * reload a record array after the `store.findAll` method resolves + * with a cached record array. + */ + shouldBackgroundReloadAll( + store: Store, + snapshotRecordArray: SnapshotRecordArray + ): boolean; + } + /** + * `DS.Serializer` is an abstract base class that you should override in your + * application to customize it for your backend. The minimum set of methods + * that you should implement is: + */ + class Serializer extends Ember.Object { + /** + * The `store` property is the application's `store` that contains + * all records. It can be used to look up serializers for other model + * types that may be nested inside the payload response. + */ + store: Store; + /** + * The `normalizeResponse` method is used to normalize a payload from the + * server to a JSON-API Document. + */ + normalizeResponse( + store: Store, + primaryModelClass: Model, + payload: {}, + id: string | number, + requestType: string + ): {}; + /** + * The `serialize` method is used when a record is saved in order to convert + * the record into the form that your external data source expects. + */ + serialize(snapshot: Snapshot, options: {}): {}; + /** + * The `normalize` method is used to convert a payload received from your + * external data source into the normalized form `store.push()` expects. You + * should override this method, munge the hash and return the normalized + * payload. + */ + normalize(typeClass: Model, hash: {}): {}; + } +} + +export default DS; + +declare module 'ember' { + namespace Ember { + /* + * The store is automatically injected into these objects + * + * https://github.com/emberjs/data/blob/05e95280e11c411177f2fbcb65fd83488d6a9d89/addon/setup-container.js#L71-L78 + */ + interface Route { + store: DS.Store; + } + interface Controller { + store: DS.Store; + } + interface DataAdapter { + store: DS.Store; + } + } + + // It is also available to inject anywhere +} + +declare module '@ember/service' { + interface Registry { + 'store': DS.Store; + } +} + +declare module 'ember-test-helpers' { + interface TestContext { + store: DS.Store; + } +} diff --git a/types/ember-data/v2/model.d.ts b/types/ember-data/v2/model.d.ts new file mode 100644 index 0000000000..026a70f534 --- /dev/null +++ b/types/ember-data/v2/model.d.ts @@ -0,0 +1,4 @@ +import { DS } from 'ember-data'; + +export default DS.Model; +export { ModelRegistry } from 'ember-data'; diff --git a/types/ember-data/v2/relationships.d.ts b/types/ember-data/v2/relationships.d.ts new file mode 100644 index 0000000000..033e07da29 --- /dev/null +++ b/types/ember-data/v2/relationships.d.ts @@ -0,0 +1,4 @@ +import DS from 'ember-data'; + +declare const hasMany: typeof DS.hasMany; +declare const belongsTo: typeof DS.belongsTo; diff --git a/types/ember-data/v2/serializer.d.ts b/types/ember-data/v2/serializer.d.ts new file mode 100644 index 0000000000..998f50c1a2 --- /dev/null +++ b/types/ember-data/v2/serializer.d.ts @@ -0,0 +1,4 @@ +import DS from 'ember-data'; + +export default DS.Serializer; +export { SerializerRegistry } from 'ember-data'; diff --git a/types/ember-data/v2/serializers/embedded-records-mixin.d.ts b/types/ember-data/v2/serializers/embedded-records-mixin.d.ts new file mode 100644 index 0000000000..209a3cd920 --- /dev/null +++ b/types/ember-data/v2/serializers/embedded-records-mixin.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.EmbeddedRecordsMixin; diff --git a/types/ember-data/v2/serializers/json-api.d.ts b/types/ember-data/v2/serializers/json-api.d.ts new file mode 100644 index 0000000000..3619a4aa2b --- /dev/null +++ b/types/ember-data/v2/serializers/json-api.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.JSONAPISerializer; diff --git a/types/ember-data/v2/serializers/json.d.ts b/types/ember-data/v2/serializers/json.d.ts new file mode 100644 index 0000000000..b1ddf2c768 --- /dev/null +++ b/types/ember-data/v2/serializers/json.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.JSONSerializer; diff --git a/types/ember-data/v2/serializers/rest.d.ts b/types/ember-data/v2/serializers/rest.d.ts new file mode 100644 index 0000000000..114c96ff7b --- /dev/null +++ b/types/ember-data/v2/serializers/rest.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.RESTSerializer; diff --git a/types/ember-data/v2/store.d.ts b/types/ember-data/v2/store.d.ts new file mode 100644 index 0000000000..e222f3c06f --- /dev/null +++ b/types/ember-data/v2/store.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.Store; diff --git a/types/ember-data/v2/test/adapter.ts b/types/ember-data/v2/test/adapter.ts new file mode 100644 index 0000000000..101ac3e42f --- /dev/null +++ b/types/ember-data/v2/test/adapter.ts @@ -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'); + } +}); diff --git a/types/ember-data/v2/test/belongs-to.ts b/types/ember-data/v2/test/belongs-to.ts new file mode 100644 index 0000000000..31977c148c --- /dev/null +++ b/types/ember-data/v2/test/belongs-to.ts @@ -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.get('parent')); +assertType(folder.get('parent').get('name')); +folder.get('parent').then(parent => { + assertType(parent); + assertType(parent.get('name')); + folder.set('parent', parent); +}); + +folder.set('parent', folder); +folder.set('parent', folder.get('parent')); +folder.set('parent', store.findRecord('folder', 3)); diff --git a/types/ember-data/v2/test/error.ts b/types/ember-data/v2/test/error.ts new file mode 100644 index 0000000000..b8b65ca967 --- /dev/null +++ b/types/ember-data/v2/test/error.ts @@ -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(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(AbortError); + +// https://emberjs.com/api/ember-data/2.16/classes/DS.UnauthorizedError +const { UnauthorizedError } = DS; +assertType(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(ForbiddenError); + +// https://emberjs.com/api/ember-data/2.16/classes/DS.NotFoundError +const { NotFoundError } = DS; +assertType(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(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(ServerError); diff --git a/types/ember-data/v2/test/has-many.ts b/types/ember-data/v2/test/has-many.ts new file mode 100644 index 0000000000..480a6bd56d --- /dev/null +++ b/types/ember-data/v2/test/has-many.ts @@ -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>(blogPost.get('commentsSync').reload()); +assertType(blogPost.get('commentsSync').createRecord()); + +const comment = blogPost.get('commentsSync').get('firstObject'); +assertType(comment); +if (comment) { + assertType(comment.get('text')); +} + +assertType>(blogPost.get('commentsAsync').reload()); +assertType(blogPost.get('commentsAsync').createRecord()); +assertType(blogPost.get('commentsAsync').get('firstObject')); + +const commentAsync = blogPost.get('commentsAsync').get('firstObject'); +assertType(commentAsync); +if (commentAsync) { + assertType(commentAsync.get('text')); +} +assertType(blogPost.get('commentsAsync').get('isFulfilled')); + +blogPost.get('commentsAsync').then(comments => { + assertType(comments.get('firstObject')); + assertType(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 }); +} diff --git a/types/ember-data/v2/test/injections.ts b/types/ember-data/v2/test/injections.ts new file mode 100644 index 0000000000..3e32a2b805 --- /dev/null +++ b/types/ember-data/v2/test/injections.ts @@ -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); + } +}); diff --git a/types/ember-data/v2/test/lib/assert.ts b/types/ember-data/v2/test/lib/assert.ts new file mode 100644 index 0000000000..10094b9616 --- /dev/null +++ b/types/ember-data/v2/test/lib/assert.ts @@ -0,0 +1,2 @@ +/** Static assertion that `value` has type `T` */ +export declare function assertType(value: T): void; diff --git a/types/ember-data/v2/test/model.ts b/types/ember-data/v2/test/model.ts new file mode 100644 index 0000000000..2085daf90a --- /dev/null +++ b/types/ember-data/v2/test/model.ts @@ -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(user.get('id')); +assertType(user.get('username')); +assertType(user.get('verified')); +assertType(user.get('createdAt')); + +user.serialize(); +user.serialize({ includeId: true }); +user.serialize({ includeId: true }); + +const attributes = user.changedAttributes(); +assertType(attributes); diff --git a/types/ember-data/v2/test/module-api.ts b/types/ember-data/v2/test/module-api.ts new file mode 100644 index 0000000000..3ef3f3a224 --- /dev/null +++ b/types/ember-data/v2/test/module-api.ts @@ -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(Adapter); +assertType(RESTAdapter); +assertType(JSONAPIAdapter); +// - inheritance +assertType(RESTAdapter); +assertType(JSONAPIAdapter); + +// SERIALIZERS +// - identity +assertType(Serializer); +assertType(RESTSerializer); +assertType(JSONSerializer); +assertType(JSONAPISerializer); +// - inheritance +assertType(JSONSerializer); +assertType(RESTSerializer); +assertType(JSONAPISerializer); + +// MODEL +// - identity +assertType(Model); +// - attributes +assertType(attr); +// - relationships +assertType(hasMany); +assertType(belongsTo); + +// TRANSFORMS +// - identity +assertType(BooleanTransform); +assertType(NumberTransform); +assertType(StringTransform); +assertType(DateTransform); +assertType(Transform); + +// STORE +// - identity +assertType(Store); + +// ERRORS +// - identity +assertType(EDErrors.AdapterError); +assertType(EDErrors.InvalidError); +assertType(EDErrors.UnauthorizedError); +assertType(EDErrors.ForbiddenError); +assertType(EDErrors.NotFoundError); +assertType(EDErrors.ConflictError); +assertType(EDErrors.ServerError); +assertType(EDErrors.TimeoutError); +assertType(EDErrors.AbortError); +assertType(EDErrors.errorsHashToArray); +assertType(EDErrors.errorsArrayToHash); diff --git a/types/ember-data/v2/test/record-reference.ts b/types/ember-data/v2/test/record-reference.ts new file mode 100644 index 0000000000..f5bf973402 --- /dev/null +++ b/types/ember-data/v2/test/record-reference.ts @@ -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); +} + +// get the identifier of the reference +if (userRef.remoteType() === 'id') { + let id = userRef.id(); + assertType(id); +} + +// load user (via store.find) +userRef.load().then(user => { + assertType(user); +}); + +// or trigger a reload +userRef.reload().then(user => { + assertType(user); +}); + +// provide data for reference +userRef.push({ id: 1, username: '@user' }).then(function(user) { + assertType(user); + userRef.value() === user; +}); diff --git a/types/ember-data/v2/test/relationships.ts b/types/ember-data/v2/test/relationships.ts new file mode 100644 index 0000000000..06795c0e26 --- /dev/null +++ b/types/ember-data/v2/test/relationships.ts @@ -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'); diff --git a/types/ember-data/v2/test/serializer.ts b/types/ember-data/v2/test/serializer.ts new file mode 100644 index 0000000000..b694b38b89 --- /dev/null +++ b/types/ember-data/v2/test/serializer.ts @@ -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; + }, +}); diff --git a/types/ember-data/v2/test/store.ts b/types/ember-data/v2/test/store.ts new file mode 100644 index 0000000000..f512afd8a7 --- /dev/null +++ b/types/ember-data/v2/test/store.ts @@ -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(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(this.get('store').findRecord('user', 123)); + assertType>(this.get('store').findAll('user')); + } +}); + +const MyRouteAsync = Ember.Route.extend({ + async beforeModel(): Promise> { + const store = Ember.get(this, 'store'); + return await store.findAll('post-comment'); + }, + async model(): Promise { + const store = this.get('store'); + return await store.findRecord('post-comment', 1); + }, + async afterModel(): Promise> { + const post = await this.get('store').findRecord('post', 1); + return await post.get('comments'); + } +}); + +class MyRouteAsyncES6 extends Ember.Route { + async beforeModel(): Promise> { + return await this.store.findAll('post-comment'); + } + async model(): Promise { + return await this.store.findRecord('post-comment', 1); + } + async afterModel(): Promise> { + 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(store.adapterFor('user')); +assertType(store.serializerFor('user')); diff --git a/types/ember-data/v2/test/transform.ts b/types/ember-data/v2/test/transform.ts new file mode 100644 index 0000000000..b76d40a73f --- /dev/null +++ b/types/ember-data/v2/test/transform.ts @@ -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] }); + } +}); diff --git a/types/ember-data/v2/transform.d.ts b/types/ember-data/v2/transform.d.ts new file mode 100644 index 0000000000..b57b7cbe76 --- /dev/null +++ b/types/ember-data/v2/transform.d.ts @@ -0,0 +1,2 @@ +import DS from 'ember-data'; +export default DS.Transform; diff --git a/types/ember-data/v2/transforms/boolean.d.ts b/types/ember-data/v2/transforms/boolean.d.ts new file mode 100644 index 0000000000..0dd20d73f2 --- /dev/null +++ b/types/ember-data/v2/transforms/boolean.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.BooleanTransform; diff --git a/types/ember-data/v2/transforms/date.d.ts b/types/ember-data/v2/transforms/date.d.ts new file mode 100644 index 0000000000..54b5e05877 --- /dev/null +++ b/types/ember-data/v2/transforms/date.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.DateTransform; diff --git a/types/ember-data/v2/transforms/number.d.ts b/types/ember-data/v2/transforms/number.d.ts new file mode 100644 index 0000000000..e3342141c1 --- /dev/null +++ b/types/ember-data/v2/transforms/number.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.NumberTransform; diff --git a/types/ember-data/v2/transforms/string.d.ts b/types/ember-data/v2/transforms/string.d.ts new file mode 100644 index 0000000000..9b0d039208 --- /dev/null +++ b/types/ember-data/v2/transforms/string.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.StringTransform; diff --git a/types/ember-data/v2/transforms/transform.d.ts b/types/ember-data/v2/transforms/transform.d.ts new file mode 100644 index 0000000000..438d8e0de5 --- /dev/null +++ b/types/ember-data/v2/transforms/transform.d.ts @@ -0,0 +1,3 @@ +import DS from 'ember-data'; + +export default DS.Transform; diff --git a/types/ember-data/v2/tsconfig.json b/types/ember-data/v2/tsconfig.json new file mode 100644 index 0000000000..003157a332 --- /dev/null +++ b/types/ember-data/v2/tsconfig.json @@ -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" + ] +} diff --git a/types/ember-data/v2/tslint.json b/types/ember-data/v2/tslint.json new file mode 100644 index 0000000000..70eef4d46e --- /dev/null +++ b/types/ember-data/v2/tslint.json @@ -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 + } +} diff --git a/types/ember-feature-flags/index.d.ts b/types/ember-feature-flags/index.d.ts index 94b443afae..6f3f963919 100644 --- a/types/ember-feature-flags/index.d.ts +++ b/types/ember-feature-flags/index.d.ts @@ -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 +// Mike North // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 2.8 import Ember from 'ember'; diff --git a/types/ember-feature-flags/v3/ember-feature-flags-tests.ts b/types/ember-feature-flags/v3/ember-feature-flags-tests.ts new file mode 100644 index 0000000000..bfcfaae62e --- /dev/null +++ b/types/ember-feature-flags/v3/ember-feature-flags-tests.ts @@ -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(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(features.get('someFeature')); diff --git a/types/ember-feature-flags/v3/index.d.ts b/types/ember-feature-flags/v3/index.d.ts new file mode 100644 index 0000000000..a2b31f3303 --- /dev/null +++ b/types/ember-feature-flags/v3/index.d.ts @@ -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 +// 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; +} diff --git a/types/ember-feature-flags/v3/tests/helpers/with-feature.d.ts b/types/ember-feature-flags/v3/tests/helpers/with-feature.d.ts new file mode 100644 index 0000000000..ef735f7923 --- /dev/null +++ b/types/ember-feature-flags/v3/tests/helpers/with-feature.d.ts @@ -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; diff --git a/types/ember-feature-flags/v3/tsconfig.json b/types/ember-feature-flags/v3/tsconfig.json new file mode 100644 index 0000000000..62622d02af --- /dev/null +++ b/types/ember-feature-flags/v3/tsconfig.json @@ -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" + ] +} diff --git a/types/ember-feature-flags/v3/tslint.json b/types/ember-feature-flags/v3/tslint.json new file mode 100644 index 0000000000..4c4fc86ace --- /dev/null +++ b/types/ember-feature-flags/v3/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "strict-export-declare-modifiers": false + } +} diff --git a/types/ember-mocha/index.d.ts b/types/ember-mocha/index.d.ts index 638edb8542..69d2202f04 100644 --- a/types/ember-mocha/index.d.ts +++ b/types/ember-mocha/index.d.ts @@ -2,8 +2,9 @@ // Project: https://github.com/emberjs/ember-mocha#readme // Definitions by: Derek Wickern // Simon Ihmig +// 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'; diff --git a/types/ember-modal-dialog/index.d.ts b/types/ember-modal-dialog/index.d.ts index b9b356e988..19f4c5692c 100644 --- a/types/ember-modal-dialog/index.d.ts +++ b/types/ember-modal-dialog/index.d.ts @@ -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 +// 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'; diff --git a/types/ember-modal-dialog/v2/ember-modal-dialog-tests.ts b/types/ember-modal-dialog/v2/ember-modal-dialog-tests.ts new file mode 100644 index 0000000000..06ab7fd241 --- /dev/null +++ b/types/ember-modal-dialog/v2/ember-modal-dialog-tests.ts @@ -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 + } +}) {} diff --git a/types/ember-modal-dialog/v2/index.d.ts b/types/ember-modal-dialog/v2/index.d.ts new file mode 100644 index 0000000000..60d1a17c1b --- /dev/null +++ b/types/ember-modal-dialog/v2/index.d.ts @@ -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 +// 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; + } +} diff --git a/types/ember-modal-dialog/v2/tsconfig.json b/types/ember-modal-dialog/v2/tsconfig.json new file mode 100644 index 0000000000..b26dfa6212 --- /dev/null +++ b/types/ember-modal-dialog/v2/tsconfig.json @@ -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" + ] +} diff --git a/types/ember-modal-dialog/v2/tslint.json b/types/ember-modal-dialog/v2/tslint.json new file mode 100644 index 0000000000..f55a9ded82 --- /dev/null +++ b/types/ember-modal-dialog/v2/tslint.json @@ -0,0 +1,8 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "no-single-declare-module": false, + "no-declare-current-package": false, + "strict-export-declare-modifiers": false + } +} diff --git a/types/ember-qunit/index.d.ts b/types/ember-qunit/index.d.ts index 5dbfb49401..ed8ac13916 100644 --- a/types/ember-qunit/index.d.ts +++ b/types/ember-qunit/index.d.ts @@ -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 +// Mike North // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 2.8 /// diff --git a/types/ember-qunit/v2/index.d.ts b/types/ember-qunit/v2/index.d.ts index 0edc5c837f..b60f73f7cc 100644 --- a/types/ember-qunit/v2/index.d.ts +++ b/types/ember-qunit/v2/index.d.ts @@ -1,6 +1,7 @@ // Type definitions for ember-qunit 2.2 // Project: https://github.com/emberjs/ember-qunit#readme // Definitions by: Derek Wickern +// Mike North // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.4 diff --git a/types/ember-qunit/v2/tsconfig.json b/types/ember-qunit/v2/tsconfig.json index 1a71d59c4a..181f10f533 100644 --- a/types/ember-qunit/v2/tsconfig.json +++ b/types/ember-qunit/v2/tsconfig.json @@ -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" ] -} \ No newline at end of file +} diff --git a/types/ember-resolver/index.d.ts b/types/ember-resolver/index.d.ts index 7c9ac5b6d0..4fe466ae13 100644 --- a/types/ember-resolver/index.d.ts +++ b/types/ember-resolver/index.d.ts @@ -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 +// Mike North // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 2.8 /// diff --git a/types/ember-resolver/v4/ember-resolver-tests.ts b/types/ember-resolver/v4/ember-resolver-tests.ts new file mode 100644 index 0000000000..d22a0d31be --- /dev/null +++ b/types/ember-resolver/v4/ember-resolver-tests.ts @@ -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 +}); diff --git a/types/ember-resolver/v4/index.d.ts b/types/ember-resolver/v4/index.d.ts new file mode 100644 index 0000000000..8047dc1bc2 --- /dev/null +++ b/types/ember-resolver/v4/index.d.ts @@ -0,0 +1,15 @@ +// Type definitions for ember-resolver 4.5 +// Project: https://github.com/ember-cli/ember-resolver#readme +// Definitions by: Dan Freeman +// Mike North +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.4 + +/// + +import Resolver from '@ember/application/resolver'; + +/** + * An Ember `Resolver` implementation used by ember-cli. + */ +export default class EmberResolver extends Resolver {} diff --git a/types/ember-resolver/v4/tsconfig.json b/types/ember-resolver/v4/tsconfig.json new file mode 100644 index 0000000000..2539233e16 --- /dev/null +++ b/types/ember-resolver/v4/tsconfig.json @@ -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" + ] +} diff --git a/types/ember-resolver/v4/tslint.json b/types/ember-resolver/v4/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/ember-resolver/v4/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" } diff --git a/types/ember-test-helpers/index.d.ts b/types/ember-test-helpers/index.d.ts index c0d5947aba..e70e76e821 100644 --- a/types/ember-test-helpers/index.d.ts +++ b/types/ember-test-helpers/index.d.ts @@ -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 +// 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 /// diff --git a/types/ember-test-helpers/v0/ember-test-helpers-tests.ts b/types/ember-test-helpers/v0/ember-test-helpers-tests.ts new file mode 100644 index 0000000000..705d0c76b1 --- /dev/null +++ b/types/ember-test-helpers/v0/ember-test-helpers-tests.ts @@ -0,0 +1,53 @@ +/// +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`

Hello!

`); + + assert.equal(this.element.textContent, 'Hello!'); +}); diff --git a/types/ember-test-helpers/v0/index.d.ts b/types/ember-test-helpers/v0/index.d.ts new file mode 100644 index 0000000000..c2c82ed6fe --- /dev/null +++ b/types/ember-test-helpers/v0/index.d.ts @@ -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 +// Mike North +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.4 + +/// + +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(...keys: K[]): Pick; + set(key: string, value: V): V; + setProperties

(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; + 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; + 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; + teardown(assert?: any): RSVP.Promise; + 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; +} + +declare module 'ember-test-helpers/has-ember-version' { + export default function hasEmberVersion(major: number, minor: number): boolean; +} diff --git a/types/ember-test-helpers/v0/tsconfig.json b/types/ember-test-helpers/v0/tsconfig.json new file mode 100644 index 0000000000..8d5222187c --- /dev/null +++ b/types/ember-test-helpers/v0/tsconfig.json @@ -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" + ] +} diff --git a/types/ember-test-helpers/v0/tslint.json b/types/ember-test-helpers/v0/tslint.json new file mode 100644 index 0000000000..659431c9ea --- /dev/null +++ b/types/ember-test-helpers/v0/tslint.json @@ -0,0 +1,8 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "strict-export-declare-modifiers": false, + "no-duplicate-imports": false, + "no-declare-current-package": false + } +} diff --git a/types/ember/index.d.ts b/types/ember/index.d.ts index 93babaa0a7..89532451b3 100755 --- a/types/ember/index.d.ts +++ b/types/ember/index.d.ts @@ -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 // bttf @@ -9,7 +9,7 @@ // Alex LaFroscia // Mike North // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 2.8 /// /// @@ -30,8 +30,19 @@ declare module 'ember' { /** * Deconstructs computed properties into the types which would be returned by `.get()`. */ - type ComputedPropertyGetters = { [K in keyof T]: Ember.ComputedProperty | ModuleComputed | T[K] }; - type ComputedPropertySetters = { [K in keyof T]: Ember.ComputedProperty | ModuleComputed | T[K] }; + type UnwrapComputedPropertyGetter = + T extends Ember.ComputedProperty ? U : + T; + type UnwrapComputedPropertyGetters = { + [P in keyof T]: UnwrapComputedPropertyGetter; + }; + + type UnwrapComputedPropertySetter = + T extends Ember.ComputedProperty ? U : + T; + type UnwrapComputedPropertySetters = { + [P in keyof T]: UnwrapComputedPropertySetter; + }; /** * 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 { + // 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(this: EmberClassConstructor): Fix; + static create(this: Class): InstanceType; - static create>( - this: EmberClassConstructor>, - arg1: T1 & ThisType> - ): Fix; + static create>> + >(this: Class, + arg1: T1 & ThisType> + ): InstanceType & T1; - static create< - Instance, - Args, - T1 extends EmberInstanceArguments, - T2 extends EmberInstanceArguments - >( - this: EmberClassConstructor>, - arg1: T1 & ThisType>, - arg2: T2 & ThisType> - ): Fix; + static create>>, + T2 extends EmberInstanceArguments>> + >(this: Class, + arg1: T1 & ThisType>, + arg2: T2 & ThisType> + ): InstanceType & T1 & T2; - static create< - Instance, - Args, - T1 extends EmberInstanceArguments, - T2 extends EmberInstanceArguments, - T3 extends EmberInstanceArguments - >( - this: EmberClassConstructor>, - arg1: T1 & ThisType>, - arg2: T2 & ThisType>, - arg3: T3 & ThisType> - ): Fix; + static create>>, + T2 extends EmberInstanceArguments>>, + T3 extends EmberInstanceArguments>> + >(this: Class, + arg1: T1 & ThisType>, + arg2: T2 & ThisType>, + arg3: T3 & ThisType> + ): InstanceType & T1 & T2 & T3; static extend( this: Statics & EmberClassConstructor @@ -1677,29 +1687,27 @@ declare module 'ember' { /** * Retrieves the value of a property from the object. */ - get(this: ComputedPropertyGetters, key: K): T[K]; + get(key: K): UnwrapComputedPropertyGetter; /** * To get the values of multiple properties at once, call `getProperties` * with a list of strings or an array: */ - getProperties(this: ComputedPropertyGetters, list: K[]): Pick; - getProperties( - this: ComputedPropertyGetters, + getProperties(list: K[]): Pick< UnwrapComputedPropertyGetters, K>; + getProperties( ...list: K[] - ): Pick; + ): Pick< UnwrapComputedPropertyGetters, K>; /** * Sets the provided key or path to the value. */ - set(this: ComputedPropertySetters, key: K, value: T[K]): T[K]; + set(key: K, value: UnwrapComputedPropertySetter): UnwrapComputedPropertySetter; /** * Sets a list of properties at once. These properties are set inside * a single `beginPropertyChanges` and `endPropertyChanges` batch, so * observers will be buffered. */ - setProperties( - this: ComputedPropertySetters, - hash: Pick - ): Pick; + setProperties( + hash: Pick, K> + ): Pick< UnwrapComputedPropertySetters, 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( - this: ComputedPropertyGetters, + getWithDefault( key: K, - defaultValue: T[K] - ): T[K]; + defaultValue: UnwrapComputedPropertyGetter + ): UnwrapComputedPropertyGetter; /** * 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(this: ComputedPropertyGetters, key: K): T[K] | undefined; + cacheFor(key: K): UnwrapComputedPropertyGetter | undefined; } const Observable: Mixin; /** @@ -3046,9 +3053,9 @@ declare module 'ember' { * it to be created. */ function cacheFor( - obj: ComputedPropertyGetters, + obj: T, key: K - ): T[K] | undefined; + ): UnwrapComputedPropertyGetter | 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(obj: T, list: K[]): Pick, K>; // for dynamic K function getProperties( - obj: ComputedPropertyGetters, - list: K[] - ): Pick; - function getProperties(obj: T, list: K[]): Pick; // for dynamic K - function getProperties( - obj: ComputedPropertyGetters, + obj: T, ...list: K[] - ): Pick; - function getProperties(obj: T, ...list: K[]): Pick; // for dynamic K + ): Pick, 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(obj: ComputedPropertyGetters, key: K): T[K]; - function get(obj: T, key: K): T[K]; // for dynamic K + function get(obj: T, key: K): UnwrapComputedPropertyGetter; /** * Retrieves the value of a property from an Object, or a default value in the * case that the property returns `undefined`. */ function getWithDefault( - obj: ComputedPropertyGetters, + obj: T, key: K, - defaultValue: T[K] - ): T[K]; - function getWithDefault(obj: T, key: K, defaultValue: T[K]): T[K]; // for dynamic K + defaultValue: UnwrapComputedPropertyGetter + ): UnwrapComputedPropertyGetter; /** * 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( - obj: ComputedPropertySetters, + function set( + obj: T, key: K, - value: V - ): V; - function set(obj: T, key: K, value: V): V; // for dynamic K + value: UnwrapComputedPropertySetter + ): UnwrapComputedPropertyGetter; /** * 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( - obj: ComputedPropertySetters, - hash: Pick - ): Pick; - function setProperties(obj: T, hash: Pick): Pick; // for dynamic K + obj: T, + hash: Pick, K> + ): Pick, 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 extends Ember.ComputedProperty { } + type ComputedProperty = Ember.ComputedProperty; + 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; diff --git a/types/ember/test/access-modifier.ts b/types/ember/test/access-modifier.ts new file mode 100644 index 0000000000..5e998f72d3 --- /dev/null +++ b/types/ember/test/access-modifier.ts @@ -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(f.hello()); +assertType(f.bar()); // $ExpectError +assertType(f.baz()); // $ExpectError + +class Foo2 extends Ember.Object.extend({ + bar: '' +}) { + hello() { return 'world'; } + protected bar() { return 'bar'; } // $ExpectError + private baz() { return 'baz'; } +} diff --git a/types/ember/test/create-negative.ts b/types/ember/test/create-negative.ts new file mode 100644 index 0000000000..234b737a6f --- /dev/null +++ b/types/ember/test/create-negative.ts @@ -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>(p4.fullName); // $ExpectError diff --git a/types/ember/test/create.ts b/types/ember/test/create.ts index 6dc61b80f6..fb32fc8166 100755 --- a/types/ember/test/create.ts +++ b/types/ember/test/create.ts @@ -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(o); +// object returned by create type-checks as an instance of Ember.Object +assertType(o.isDestroyed); // from instance +assertType(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(o1.x); +assertType(o1.y); +o1.y; // $ExpectType string +o1.z; // $ExpectType boolean + const obj = Ember.Object.create({ a: 1 }, { b: 2 }, { c: 3 }); -assertType(obj.a); assertType(obj.b); +assertType(obj.a); assertType(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(p.firstName); +assertType>(p.fullName); +assertType(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(p4.firstName); +assertType(p4.fullName); diff --git a/types/ember/test/reopen.ts b/types/ember/test/reopen.ts index 6c13ca4155..ee5ee04edc 100755 --- a/types/ember/test/reopen.ts +++ b/types/ember/test/reopen.ts @@ -29,6 +29,9 @@ assertType(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" diff --git a/types/ember/tsconfig.json b/types/ember/tsconfig.json index 676c107f72..4ff7e7bf07 100755 --- a/types/ember/tsconfig.json +++ b/types/ember/tsconfig.json @@ -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", diff --git a/types/ember/v2/index.d.ts b/types/ember/v2/index.d.ts new file mode 100755 index 0000000000..93babaa0a7 --- /dev/null +++ b/types/ember/v2/index.d.ts @@ -0,0 +1,3898 @@ +// Type definitions for Ember.js 2.8 +// Project: http://emberjs.com/ +// Definitions by: Jed Mao +// bttf +// Derek Wickern +// Chris Krycho +// Theron Cross +// Martin Feckie +// Alex LaFroscia +// Mike North +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.4 + +/// +/// + +declare module 'ember' { + // Capitalization is intentional: this makes it much easier to re-export RSVP on + // the Ember namespace. + import Rsvp from 'rsvp'; + import { TemplateFactory } from 'htmlbars-inline-precompile'; + + import { Registry as ServiceRegistry } from '@ember/service'; + import { Registry as ControllerRegistry } from '@ember/controller'; + import ModuleComputed from '@ember/object/computed'; + + // Get an alias to the global Array type to use in inner scope below. + type GlobalArray = T[]; + + /** + * Deconstructs computed properties into the types which would be returned by `.get()`. + */ + type ComputedPropertyGetters = { [K in keyof T]: Ember.ComputedProperty | ModuleComputed | T[K] }; + type ComputedPropertySetters = { [K in keyof T]: Ember.ComputedProperty | ModuleComputed | T[K] }; + + /** + * Check that any arguments to `create()` match the type's properties. + * + * Accept any additional properties and add merge them into the instance. + */ + type EmberInstanceArguments = Partial & { + [key: string]: any; + }; + + /** + * Accept any additional properties and add merge them into the prototype. + */ + interface EmberClassArguments { + [key: string]: any; + } + + /** + * Map type `T` to a plain object hash with the identity mapping. + * + * Discards any additional object identity like the ability to `new()` up the class. + * The `new()` capability is added back later by merging `EmberClassConstructor` + * + * Implementation is carefully chosen for the reasons described in + * https://github.com/typed-ember/ember-typings/pull/29 + */ + type Objectify = Readonly; + + type Fix = { [K in keyof T]: T[K] }; + + /** + * Ember.Object.extend(...) accepts any number of mixins or literals. + */ + type MixinOrLiteral = Ember.Mixin | T; + + /** + * Used to infer the type of ember classes of type `T`. + * + * Generally you would use `EmberClass.create()` instead of `new EmberClass()`. + * + * The single-arg constructor is required by the typescript compiler. + * The multi-arg constructor is included for better ergonomics. + * + * Implementation is carefully chosen for the reasons described in + * https://github.com/typed-ember/ember-typings/pull/29 + */ + type EmberClassConstructor = (new (properties?: object) => T) & (new (...args: any[]) => T); + + type ComputedPropertyGetterFunction = (this: any, key: string) => T; + + interface ComputedPropertyGet { + get(this: any, key: string): T; + } + + interface ComputedPropertySet { + set(this: any, key: string, value: T): T; + } + + type ComputedPropertyCallback = + | ComputedPropertyGetterFunction + | ComputedPropertyGet + | ComputedPropertySet + | (ComputedPropertyGet & ComputedPropertySet); + + interface ActionsHash { + [index: string]: (...params: any[]) => any; + } + + interface EmberRunTimer { + __ember_run_timer_brand__: any; + } + + type RunMethod = ((this: Target, ...args: any[]) => Ret) | keyof Target; + type EmberRunQueues = + | 'sync' + | 'actions' + | 'routerTransitions' + | 'render' + | 'afterRender' + | 'destroy'; + type QueryParamTypes = 'boolean' | 'number' | 'array' | 'string'; + type QueryParamScopeTypes = 'controller' | 'model'; + + type ObserverMethod = + | (keyof Target) + | ((this: Target, sender: Sender, key: keyof Sender, value: any, rev: number) => void); + + interface RenderOptions { + into?: string; + controller?: string; + model?: any; + outlet?: string; + view?: string; + } + + interface RouteQueryParam { + refreshModel?: boolean; + replace?: boolean; + as?: string; + } + + interface EventDispatcherEvents { + touchstart?: string | null; + touchmove?: string | null; + touchend?: string | null; + touchcancel?: string | null; + keydown?: string | null; + keyup?: string | null; + keypress?: string | null; + mousedown?: string | null; + mouseup?: string | null; + contextmenu?: string | null; + click?: string | null; + dblclick?: string | null; + mousemove?: string | null; + focusin?: string | null; + focusout?: string | null; + mouseenter?: string | null; + mouseleave?: string | null; + submit?: string | null; + input?: string | null; + change?: string | null; + dragstart?: string | null; + drag?: string | null; + dragenter?: string | null; + dragleave?: string | null; + dragover?: string | null; + drop?: string | null; + dragend?: string | null; + [event: string]: string | null | undefined; + } + + interface ViewMixin { + /** + * A list of properties of the view to apply as attributes. If the property + * is a string value, the value of that string will be applied as the value + * for an attribute of the property's name. + */ + attributeBindings: string[]; + /** + * Returns the current DOM element for the view. + */ + element: Element; + /** + * Returns a jQuery object for this view's element. If you pass in a selector + * string, this method will return a jQuery object, using the current element + * as its buffer. + */ + $: JQueryStatic; + /** + * The HTML `id` of the view's element in the DOM. You can provide this + * value yourself but it must be unique (just as in HTML): + */ + elementId: string; + /** + * Tag name for the view's outer element. The tag name is only used when an + * element is first created. If you change the `tagName` for an element, you + * must destroy and recreate the view element. + */ + tagName: string; + /** + * Renders the view again. This will work regardless of whether the + * view is already in the DOM or not. If the view is in the DOM, the + * rendering process will be deferred to give bindings a chance + * to synchronize. + */ + rerender(): void; + /** + * Called when a view is going to insert an element into the DOM. + */ + willInsertElement(): void; + /** + * Called when the element of the view has been inserted into the DOM. + * Override this function to do any set up that requires an element + * in the document body. + */ + didInsertElement(): void; + /** + * Called when the view is about to rerender, but before anything has + * been torn down. This is a good opportunity to tear down any manual + * observers you have installed based on the DOM state + */ + willClearRender(): void; + /** + * Called when the element of the view is going to be destroyed. Override + * this function to do any teardown that requires an element, like removing + * event listeners. + */ + willDestroyElement(): void; + } + const ViewMixin: Ember.Mixin; + + /** + Ember.CoreView is an abstract class that exists to give view-like behavior to both Ember's main + view class Ember.Component and other classes that don't need the full functionality of Ember.Component. + + Unless you have specific needs for CoreView, you will use Ember.Component in your applications. + **/ + class CoreView extends Ember.Object.extend(Ember.Evented, Ember.ActionHandler) {} + interface ActionSupport { + sendAction(action: string, ...params: any[]): void; + } + const ActionSupport: Ember.Mixin; + + interface ClassNamesSupport { + /** + A list of properties of the view to apply as class names. If the property is a string value, + the value of that string will be applied as a class name. + + If the value of the property is a Boolean, the name of that property is added as a dasherized + class name. + + If you would prefer to use a custom value instead of the dasherized property name, you can + pass a binding like this: `classNameBindings: ['isUrgent:urgent']` + + This list of properties is inherited from the component's superclasses as well. + */ + classNameBindings: string[]; + /** + * Standard CSS class names to apply to the view's outer element. This + * property automatically inherits any class names defined by the view's + * superclasses as well. + */ + classNames: string[]; + } + const ClassNamesSupport: Ember.Mixin; + + interface TriggerActionOptions { + action?: string; + target?: Ember.Object; + actionContext?: Ember.Object; + } + /** + Ember.TargetActionSupport is a mixin that can be included in a class to add a triggerAction method + with semantics similar to the Handlebars {{action}} helper. In normal Ember usage, the {{action}} + helper is usually the best choice. This mixin is most often useful when you are doing more + complex event handling in Components. + **/ + interface TargetActionSupport { + triggerAction(opts: TriggerActionOptions): boolean; + } + + export namespace Ember { + interface FunctionPrototypeExtensions { + /** + * The `property` extension of Javascript's Function prototype is available + * when `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Function` is + * `true`, which is the default. + */ + property(...args: string[]): ComputedProperty; + /** + * The `observes` extension of Javascript's Function prototype is available + * when `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Function` is + * true, which is the default. + */ + observes(...args: string[]): this; + /** + * The `on` extension of Javascript's Function prototype is available + * when `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Function` is + * true, which is the default. + */ + on(...args: string[]): this; + } + + interface ArrayPrototypeExtensions extends MutableArray, Observable, Copyable {} + + /** + * Given a fullName return a factory manager. + */ + interface _ContainerProxyMixin { + /** + * Returns an object that can be used to provide an owner to a + * manually created instance. + */ + ownerInjection(): {}; + /** + * Given a fullName return a corresponding instance. + */ + lookup(fullName: string, options?: {}): any; + /** + * Given a fullName return a corresponding factory. + */ + factoryFor(fullName: string, options?: {}): any; + } + const _ContainerProxyMixin: Mixin<_ContainerProxyMixin>; + + /** + * RegistryProxyMixin is used to provide public access to specific + * registry functionality. + */ + interface _RegistryProxyMixin { + /** + * Given a fullName return the corresponding factory. + */ + resolveRegistration(fullName: string): Function; + /** + * Registers a factory or value that can be used for dependency injection (with + * `inject`) or for service lookup. Each factory is registered with + * a full name including two parts: `type:name`. + */ + register(fullName: string, factory: any, options?: { singleton?: boolean, instantiate?: boolean }): any; + /** + * Unregister a factory. + */ + unregister(fullName: string): any; + /** + * Check if a factory is registered. + */ + hasRegistration(fullName: string): boolean; + /** + * Register an option for a particular factory. + */ + registerOption(fullName: string, optionName: string, options: {}): any; + /** + * Return a specific registered option for a particular factory. + */ + registeredOption(fullName: string, optionName: string): {}; + /** + * Register options for a particular factory. + */ + registerOptions(fullName: string, options: {}): any; + /** + * Return registered options for a particular factory. + */ + registeredOptions(fullName: string): {}; + /** + * Allow registering options for all factories of a type. + */ + registerOptionsForType(type: string, options: {}): any; + /** + * Return the registered options for all factories of a type. + */ + registeredOptionsForType(type: string): {}; + /** + * Define a dependency injection onto a specific factory or all factories + * of a type. + */ + inject(factoryNameOrType: string, property: string, injectionName: string): any; + } + const _RegistryProxyMixin: Mixin<_RegistryProxyMixin>; + /** + Ember.ActionHandler is available on some familiar classes including Ember.Route, + Ember.Component, and Ember.Controller. (Internally the mixin is used by Ember.CoreView, + Ember.ControllerMixin, and Ember.Route and available to the above classes through inheritance.) + **/ + interface ActionHandler { + /** + Triggers a named action on the ActionHandler. Any parameters supplied after the actionName + string will be passed as arguments to the action target function. + + If the ActionHandler has its target property set, actions may bubble to the target. + Bubbling happens when an actionName can not be found in the ActionHandler's actions + hash or if the action target function returns true. + **/ + send(actionName: string, ...args: any[]): void; + /** + The collection of functions, keyed by name, available on this ActionHandler as action targets. + **/ + actions: ActionsHash; + } + const ActionHandler: Ember.Mixin; + /** + An instance of Ember.Application is the starting point for every Ember application. It helps to + instantiate, initialize and coordinate the many objects that make up your app. + **/ + class Application extends Engine { + /** + Call advanceReadiness after any asynchronous setup logic has completed. + Each call to deferReadiness must be matched by a call to advanceReadiness + or the application will never become ready and routing will not begin. + **/ + advanceReadiness(): void; + /** + Use this to defer readiness until some condition is true. + + This allows you to perform asynchronous setup logic and defer + booting your application until the setup has finished. + + However, if the setup requires a loading UI, it might be better + to use the router for this purpose. + */ + deferReadiness(): void; + /** + defines an injection or typeInjection + **/ + inject(factoryNameOrType: string, property: string, injectionName: string): void; + /** + This injects the test helpers into the window's scope. If a function of the + same name has already been defined it will be cached (so that it can be reset + if the helper is removed with `unregisterHelper` or `removeTestHelpers`). + Any callbacks registered with `onInjectHelpers` will be called once the + helpers have been injected. + **/ + injectTestHelpers(): void; + /** + registers a factory for later injection + @param fullName type:name (e.g., 'model:user') + @param factory (e.g., App.Person) + **/ + register(fullName: string, factory: Function, options?: {}): void; + /** + This removes all helpers that have been registered, and resets and functions + that were overridden by the helpers. + **/ + removeTestHelpers(): void; + /** + Reset the application. This is typically used only in tests. + **/ + reset(): void; + /** + This hook defers the readiness of the application, so that you can start + the app when your tests are ready to run. It also sets the router's + location to 'none', so that the window's location will not be modified + (preventing both accidental leaking of state between tests and interference + with your testing framework). + **/ + setupForTesting(): void; + /** + The DOM events for which the event dispatcher should listen. + */ + customEvents: EventDispatcherEvents; + /** + The Ember.EventDispatcher responsible for delegating events to this application's views. + **/ + eventDispatcher: EventDispatcher; + /** + Set this to provide an alternate class to Ember.DefaultResolver + **/ + resolver: DefaultResolver; + /** + The root DOM element of the Application. This can be specified as an + element or a jQuery-compatible selector string. + + This is the element that will be passed to the Application's, eventDispatcher, + which sets up the listeners for event delegation. Every view in your application + should be a child of the element you specify here. + **/ + rootElement: HTMLElement | string; + /** + Called when the Application has become ready. + The call will be delayed until the DOM has become ready. + **/ + ready: Function; + /** + Application's router. + **/ + Router: Router; + registry: Registry; + /** + * Initialize the application and return a promise that resolves with the `Application` + * object when the boot process is complete. + */ + boot(): Promise; + } + /** + The `ApplicationInstance` encapsulates all of the stateful aspects of a + running `Application`. + **/ + class ApplicationInstance extends EngineInstance {} + /** + This module implements Observer-friendly Array-like behavior. This mixin is picked up by the + Array class as well as other controllers, etc. that want to appear to be arrays. + **/ + interface Array extends Enumerable { + /** + * __Required.__ You must implement this method to apply this mixin. + */ + length: number | ComputedProperty; + /** + * Returns the object at the given `index`. If the given `index` is negative + * or is greater or equal than the array length, returns `undefined`. + */ + objectAt(idx: number): T | undefined; + /** + * This returns the objects at the specified indexes, using `objectAt`. + */ + objectsAt(indexes: number[]): Ember.Array; + /** + * Returns a new array that is a slice of the receiver. This implementation + * uses the observable array methods to retrieve the objects for the new + * slice. + */ + slice(beginIndex?: number, endIndex?: number): T[]; + /** + * Returns the index of the given object's first occurrence. + * If no `startAt` argument is given, the starting location to + * search is 0. If it's negative, will count backward from + * the end of the array. Returns -1 if no match is found. + */ + indexOf(searchElement: T, fromIndex?: number): number; + /** + * Returns the index of the given object's last occurrence. + * If no `startAt` argument is given, the search starts from + * the last position. If it's negative, will count backward + * from the end of the array. Returns -1 if no match is found. + */ + lastIndexOf(searchElement: T, fromIndex?: number): number; + /** + * Adds an array observer to the receiving array. The array observer object + * normally must implement two methods: + */ + addArrayObserver(target: {}, opts: {}): this; + /** + * Removes an array observer from the object if the observer is current + * registered. Calling this method multiple times with the same object will + * have no effect. + */ + removeArrayObserver(target: {}, opts: {}): this; + /** + * Becomes true whenever the array currently has observers watching changes + * on the array. + */ + hasArrayObservers: ComputedProperty; + /** + * If you are implementing an object that supports `Ember.Array`, call this + * method just before the array content changes to notify any observers and + * invalidate any related properties. Pass the starting index of the change + * as well as a delta of the amounts to change. + */ + arrayContentWillChange(startIdx: number, removeAmt: number, addAmt: number): this; + /** + * If you are implementing an object that supports `Ember.Array`, call this + * method just after the array content changes to notify any observers and + * invalidate any related properties. Pass the starting index of the change + * as well as a delta of the amounts to change. + */ + arrayContentDidChange(startIdx: number, removeAmt: number, addAmt: number): this; + /** + * Returns a special object that can be used to observe individual properties + * on the array. Just get an equivalent property on this object and it will + * return an enumerable that maps automatically to the named key on the + * member objects. + */ + '@each': ComputedProperty; + } + // Ember.Array rather than Array because the `array-type` lint rule doesn't realize the global is shadowed + const Array: Mixin>; + + /** + An ArrayProxy wraps any other object that implements Ember.Array and/or Ember.MutableArray, + forwarding all requests. This makes it very useful for a number of binding use cases or other cases + where being able to swap out the underlying array is useful. + **/ + interface ArrayProxy extends MutableArray {} + class ArrayProxy extends Object.extend(MutableArray as {}) { + content: NativeArray; + + /** + * Should actually retrieve the object at the specified index from the + * content. You can override this method in subclasses to transform the + * content item to something new. + */ + objectAtContent(idx: number): T | undefined; + } + /** + AutoLocation will select the best location option based off browser support with the priority order: history, hash, none. + **/ + class AutoLocation extends Object {} + /** + * Connects the properties of two objects so that whenever the value of one property changes, + * the other property will be changed also. + * + * @deprecated https://emberjs.com/deprecations/v2.x#toc_ember-binding + **/ + class Binding { + constructor(toPath: string, fromPath: string); + connect(obj: any): Binding; + copy(): Binding; + disconnect(): Binding; + from(path: string): Binding; + to(path: string | string[]): Binding; + toString(): string; + } + /** + The internal class used to create text inputs when the {{input}} helper is used + with type of checkbox. See Handlebars.helpers.input for usage details. + **/ + class Checkbox extends Component {} + /** + * Implements some standard methods for comparing objects. Add this mixin to + * any class you create that can compare its instances. + */ + interface Comparable { + compare(a: any, b: any): number; + } + const Comparable: Mixin; + /** + A view that is completely isolated. Property access in its templates go to the view object + and actions are targeted at the view object. There is no access to the surrounding context or + outer controller; all contextual information is passed in. + **/ + class Component extends CoreView.extend(ViewMixin, ActionSupport, ClassNamesSupport) { + // methods + readDOMAttr(name: string): string; + // properties + /** + * The WAI-ARIA role of the control represented by this view. For example, a button may have a + * role of type 'button', or a pane may have a role of type 'alertdialog'. This property is + * used by assistive software to help visually challenged users navigate rich web applications. + */ + ariaRole: string; + /** + * The HTML id of the component's element in the DOM. You can provide this value yourself but + * it must be unique (just as in HTML): + * + * If not manually set a default value will be provided by the framework. Once rendered an + * element's elementId is considered immutable and you should never change it. If you need + * to compute a dynamic value for the elementId, you should do this when the component or + * element is being instantiated: + */ + elementId: string; + /** + * If false, the view will appear hidden in DOM. + */ + isVisible: boolean; + /** + * A component may contain a layout. A layout is a regular template but supersedes the template + * property during rendering. It is the responsibility of the layout template to retrieve the + * template property from the component (or alternatively, call Handlebars.helpers.yield, + * {{yield}}) to render it in the correct location. This is useful for a component that has a + * shared wrapper, but which delegates the rendering of the contents of the wrapper to the + * template property on a subclass. + */ + layout: TemplateFactory | string; + /** + * Enables components to take a list of parameters as arguments. + */ + static positionalParams: string[] | string; + // events + /** + * Called when the attributes passed into the component have been updated. Called both during the + * initial render of a container and during a rerender. Can be used in place of an observer; code + * placed here will be executed every time any attribute updates. + */ + didReceiveAttrs(): void; + /** + * Called after a component has been rendered, both on initial render and in subsequent rerenders. + */ + didRender(): void; + /** + * Called when the component has updated and rerendered itself. Called only during a rerender, + * not during an initial render. + */ + didUpdate(): void; + /** + * Called when the attributes passed into the component have been changed. Called only during a + * rerender, not during an initial render. + */ + didUpdateAttrs(): void; + /** + * Called before a component has been rendered, both on initial render and in subsequent rerenders. + */ + willRender(): void; + /** + * Called when the component is about to update and rerender itself. Called only during a rerender, + * not during an initial render. + */ + willUpdate(): void; + } + /** + A computed property transforms an objects function into a property. + By default the function backing the computed property will only be called once and the result + will be cached. You can specify various properties that your computed property is dependent on. + This will force the cached result to be recomputed if the dependencies are modified. + **/ + class ComputedProperty { + /** + * 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. + */ + volatile(): this; + /** + * Call on a computed property to set it into read-only mode. When in this + * mode the computed property will throw an error when set. + */ + readOnly(): this; + /** + * Sets the dependent keys on this computed property. Pass any number of + * arguments containing key paths that this computed property depends on. + */ + property(...path: string[]): this; + /** + * In some cases, you may want to annotate computed properties with additional + * metadata about how they function or what values they operate on. For example, + * computed property functions may close over variables that are then no longer + * available for introspection. + */ + meta(meta: {}): this; + meta(): {}; + } + /** + * A container used to instantiate and cache objects. + */ + class Container { + /** + * Given a fullName, return the corresponding factory. The consumer of the factory + * is responsible for the destruction of any factory instances, as there is no + * way for the container to ensure instances are destroyed when it itself is + * destroyed. + */ + factoryFor(fullName: string, options?: {}): any; + } + /** + The ContainerDebugAdapter helps the container and resolver interface + with tools that debug Ember such as the Ember Inspector for Chrome and Firefox. + **/ + class ContainerDebugAdapter extends Object { + resolver: Resolver; + canCatalogEntriesByType(type: string): boolean; + catalogEntriesByType(type: string): any[]; + } + /** + * Additional methods for the Controller. + */ + interface ControllerMixin extends ActionHandler { + replaceRoute(name: string, ...args: any[]): void; + transitionToRoute(name: string, ...args: any[]): void; + model: any; + queryParams: string | string[] | Array<{ [key: string]: { + type?: QueryParamTypes, + scope?: QueryParamScopeTypes, + as?: string + }}>; + target: Object; + } + const ControllerMixin: Ember.Mixin; + class Controller extends Object.extend(ControllerMixin) {} + /** + * Implements some standard methods for copying an object. Add this mixin to + * any object you create that can create a copy of itself. This mixin is + * added automatically to the built-in array. + */ + interface Copyable { + /** + * __Required.__ You must implement this method to apply this mixin. + */ + copy(deep: boolean): Copyable; + /** + * If the object implements `Ember.Freezable`, then this will return a new + * copy if the object is not frozen and the receiver if the object is frozen. + */ + frozenCopy(): Copyable; + } + const Copyable: Ember.Mixin; + class CoreObject { + /** + * As of Ember 3.1, CoreObject constructor takes initial object properties as an argument. + * See: https://github.com/emberjs/ember.js/commit/4709935854d4c29b0d2c054614d53fa2c55309b1 + **/ + constructor(properties?: object); + + _super(...args: any[]): any; + + /** + An overridable method called when objects are instantiated. By default, + does nothing unless it is overridden during class definition. + **/ + init(): void; + + /** + * Defines the properties that will be concatenated from the superclass (instead of overridden). + * @default null + */ + concatenatedProperties: any[]; + + /** + Destroyed object property flag. If this property is true the observers and bindings were + already removed by the effect of calling the destroy() method. + @default false + **/ + isDestroyed: boolean; + /** + Destruction scheduled flag. The destroy() method has been called. The object stays intact + until the end of the run loop at which point the isDestroyed flag is set. + @default false + **/ + isDestroying: boolean; + + /** + Destroys an object by setting the `isDestroyed` flag and removing its + metadata, which effectively destroys observers and bindings. + If you try to set a property on a destroyed object, an exception will be + raised. + Note that destruction is scheduled for the end of the run loop and does not + happen immediately. It will set an isDestroying flag immediately. + @return receiver + */ + destroy(): CoreObject; + + /** + Override to implement teardown. + */ + willDestroy(): void; + + /** + Returns a string representation which attempts to provide more information than Javascript's toString + typically does, in a generic way for all Ember objects (e.g., ""). + @return string representation + **/ + toString(): string; + + static create(this: EmberClassConstructor): Fix; + + static create>( + this: EmberClassConstructor>, + arg1: T1 & ThisType> + ): Fix; + + static create< + Instance, + Args, + T1 extends EmberInstanceArguments, + T2 extends EmberInstanceArguments + >( + this: EmberClassConstructor>, + arg1: T1 & ThisType>, + arg2: T2 & ThisType> + ): Fix; + + static create< + Instance, + Args, + T1 extends EmberInstanceArguments, + T2 extends EmberInstanceArguments, + T3 extends EmberInstanceArguments + >( + this: EmberClassConstructor>, + arg1: T1 & ThisType>, + arg2: T2 & ThisType>, + arg3: T3 & ThisType> + ): Fix; + + static extend( + this: Statics & EmberClassConstructor + ): Objectify & EmberClassConstructor; + + static extend( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static extend< + Statics, + Instance extends B1 & B2, + T1 extends EmberClassArguments, + B1, + T2 extends EmberClassArguments, + B2 + >( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static extend< + Statics, + Instance extends B1 & B2 & B3, + T1 extends EmberClassArguments, + B1, + T2 extends EmberClassArguments, + B2, + T3 extends EmberClassArguments, + B3 + >( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType>, + arg3: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static extend< + Statics, + Instance extends B1 & B2 & B3 & B4, + T1 extends EmberClassArguments, + B1, + T2 extends EmberClassArguments, + B2, + T3 extends EmberClassArguments, + B3, + T4 extends EmberClassArguments, + B4 + >( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType>, + arg3: MixinOrLiteral & ThisType>, + arg4: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static reopen( + this: Statics & EmberClassConstructor + ): Objectify & EmberClassConstructor; + + static reopen( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static reopen< + Statics, + Instance extends B1 & B2, + T1 extends EmberClassArguments, + B1, + T2 extends EmberClassArguments, + B2 + >( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static reopen< + Statics, + Instance extends B1 & B2 & B3, + T1 extends EmberClassArguments, + B1, + T2 extends EmberClassArguments, + B2, + T3 extends EmberClassArguments, + B3 + >( + this: Statics & EmberClassConstructor, + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType>, + arg3: MixinOrLiteral & ThisType> + ): Objectify & EmberClassConstructor; + + static reopenClass(this: Statics): Statics; + + static reopenClass( + this: Statics, + arg1: T1 + ): Statics & T1; + + static reopenClass< + Statics, + T1 extends EmberClassArguments, + T2 extends EmberClassArguments + >(this: Statics, arg1: T1, arg2: T2): Statics & T1 & T2; + + static reopenClass< + Statics, + T1 extends EmberClassArguments, + T2 extends EmberClassArguments, + T3 extends EmberClassArguments + >(this: Statics, arg1: T1, arg2: T2, arg3: T3): Statics & T1 & T2 & T3; + + static detect( + this: Statics & EmberClassConstructor, + obj: any + ): obj is Objectify & EmberClassConstructor; + + static detectInstance( + this: EmberClassConstructor, + obj: any + ): obj is Instance; + + /** + Iterate over each computed property for the class, passing its name and any + associated metadata (see metaForProperty) to the callback. + **/ + static eachComputedProperty(callback: Function, binding: {}): void; + /** + Returns the original hash that was passed to meta(). + @param key property name + **/ + static metaForProperty(key: string): {}; + static isClass: boolean; + static isMethod: boolean; + } + /** + * The `DataAdapter` helps a data persistence library + * interface with tools that debug Ember such as Chrome and Firefox. + */ + class DataAdapter extends Object { + /** + * The container-debug-adapter which is used + * to list all models. + */ + containerDebugAdapter: any; + /** + * Ember Data > v1.0.0-beta.18 + * requires string model names to be passed + * around instead of the actual factories. + */ + acceptsModelName: any; + /** + * Specifies how records can be filtered. + * Records returned will need to have a `filterValues` + * property with a key for every name in the returned array. + */ + getFilters(): any[]; + /** + * Fetch the model types and observe them for changes. + */ + watchModelTypes(typesAdded: Function, typesUpdated: Function): Function; + /** + * Fetch the records of a given type and observe them for changes. + */ + watchRecords( + modelName: string, + recordsAdded: Function, + recordsUpdated: Function, + recordsRemoved: Function + ): Function; + } + const Debug: { + /** + * Allows for runtime registration of handler functions that override the default deprecation behavior. + * Deprecations are invoked by calls to [Ember.deprecate](http://emberjs.com/api/classes/Ember.html#method_deprecate). + * The following example demonstrates its usage by registering a handler that throws an error if the + * message contains the word "should", otherwise defers to the default handler. + */ + registerDeprecationHandler(handler: Function): any; + /** + * Allows for runtime registration of handler functions that override the default warning behavior. + * Warnings are invoked by calls made to [Ember.warn](http://emberjs.com/api/classes/Ember.html#method_warn). + * The following example demonstrates its usage by registering a handler that does nothing overriding Ember's + * default warning behavior. + */ + registerWarnHandler(handler: Function): any; + }; + /** + * The DefaultResolver defines the default lookup rules to resolve + * container lookups before consulting the container for registered + * items: + */ + class DefaultResolver extends Resolver { + /** + * This method is called via the container's resolver method. + * It parses the provided `fullName` and then looks up and + * returns the appropriate template or class. + */ + resolve(fullName: string): {}; + /** + * This will be set to the Application instance when it is + * created. + */ + namespace: Application; + } + interface Initializer { + name: string; + before?: string[]; + after?: string[]; + initialize(application: T): void; + } + /** + * The `Engine` class contains core functionality for both applications and + * engines. + */ + class Engine extends Namespace.extend(_RegistryProxyMixin) { + /** + * The goal of initializers should be to register dependencies and injections. + * This phase runs once. Because these initializers may load code, they are + * allowed to defer application readiness and advance it. If you need to access + * the container or store you should use an InstanceInitializer that will be run + * after all initializers and therefore after all code is loaded and the app is + * ready. + */ + static initializer(initializer: Initializer): void; + /** + * Instance initializers run after all initializers have run. Because + * instance initializers run after the app is fully set up. We have access + * to the store, container, and other items. However, these initializers run + * after code has loaded and are not allowed to defer readiness. + */ + static instanceInitializer(instanceInitializer: Initializer): void; + /** + * Set this to provide an alternate class to `Ember.DefaultResolver` + */ + resolver: Resolver; + } + /** + * The `EngineInstance` encapsulates all of the stateful aspects of a + * running `Engine`. + */ + class EngineInstance extends Ember.Object.extend( + _RegistryProxyMixin, + _ContainerProxyMixin + ) { + /** + * Unregister a factory. + */ + unregister(fullName: string): any; + + /** + * Initialize the `EngineInstance` and return a promise that resolves + * with the instance itself when the boot process is complete. + */ + boot(): Promise; + } + /** + * This mixin defines the common interface implemented by enumerable objects + * in Ember. Most of these methods follow the standard Array iteration + * API defined up to JavaScript 1.8 (excluding language-specific features that + * cannot be emulated in older versions of JavaScript). + */ + interface Enumerable { + /** + * Helper method returns the first object from a collection. This is usually + * used by bindings and other parts of the framework to extract a single + * object if the enumerable contains only one item. + */ + firstObject: ComputedProperty; + /** + * Helper method returns the last object from a collection. If your enumerable + * contains only one object, this method should always return that object. + * If your enumerable is empty, this method should return `undefined`. + */ + lastObject: ComputedProperty; + /** + * @deprecated Use `Enumerable#includes` instead. + */ + contains(obj: T): boolean; + /** + * Iterates through the enumerable, calling the passed function on each + * item. This method corresponds to the `forEach()` method defined in + * JavaScript 1.6. + */ + forEach: GlobalArray['forEach']; + /** + * Alias for `mapBy` + */ + getEach(key: string): any[]; + /** + * Sets the value on the named property for each member. This is more + * ergonomic than using other methods defined on this helper. If the object + * implements Ember.Observable, the value will be changed to `set(),` otherwise + * it will be set directly. `null` objects are skipped. + */ + setEach(key: string, value: any): any; + /** + * Maps all of the items in the enumeration to another value, returning + * a new array. This method corresponds to `map()` defined in JavaScript 1.6. + */ + map: GlobalArray['map']; + /** + * Similar to map, this specialized function returns the value of the named + * property on all items in the enumeration. + */ + mapBy(key: string): any[]; + /** + * Returns an array with all of the items in the enumeration that the passed + * function returns true for. This method corresponds to `filter()` defined in + * JavaScript 1.6. + */ + filter: GlobalArray['filter']; + /** + * Returns an array with all of the items in the enumeration where the passed + * function returns false. This method is the inverse of filter(). + */ + reject(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): NativeArray; + /** + * Returns an array with just the items with the matched property. You + * can pass an optional second argument with the target value. Otherwise + * this will match any property that evaluates to `true`. + */ + filterBy(key: string, value?: any): NativeArray; + /** + * Returns an array with the items that do not have truthy values for + * key. You can pass an optional second argument with the target value. Otherwise + * this will match any property that evaluates to false. + */ + rejectBy(key: string, value?: any): NativeArray; + /** + * Returns the first item in the array for which the callback returns true. + * This method works similar to the `filter()` method defined in JavaScript 1.6 + * except that it will stop working on the array once a match is found. + */ + find: GlobalArray['find']; + /** + * Returns the first item with a property matching the passed value. You + * can pass an optional second argument with the target value. Otherwise + * this will match any property that evaluates to `true`. + */ + findBy(key: string, value?: any): T | undefined; + /** + * Returns `true` if the passed function returns true for every item in the + * enumeration. This corresponds with the `every()` method in JavaScript 1.6. + */ + every: GlobalArray['every']; + /** + * Returns `true` if the passed property resolves to the value of the second + * argument for all items in the enumerable. This method is often simpler/faster + * than using a callback. + */ + isEvery(key: string, value?: any): boolean; + /** + * Returns `true` if the passed function returns true for any item in the + * enumeration. + */ + any(callback: (value: T, index: number, array: T[]) => boolean, target?: {}): boolean; + /** + * Returns `true` if the passed property resolves to the value of the second + * argument for any item in the enumerable. This method is often simpler/faster + * than using a callback. + */ + isAny(key: string, value?: any): boolean; + /** + * This will combine the values of the enumerator into a single value. It + * is a useful way to collect a summary value from an enumeration. This + * corresponds to the `reduce()` method defined in JavaScript 1.8. + */ + reduce: GlobalArray['reduce']; + /** + * Invokes the named method on every object in the receiver that + * implements it. This method corresponds to the implementation in + * Prototype 1.6. + */ + invoke(methodName: keyof T, ...args: any[]): any[]; + /** + * Simply converts the enumerable into a genuine array. The order is not + * guaranteed. Corresponds to the method implemented by Prototype. + */ + toArray(): T[]; + /** + * Returns a copy of the array with all `null` and `undefined` elements removed. + */ + compact(): NativeArray; + /** + * Returns a new enumerable that excludes the passed value. The default + * implementation returns an array regardless of the receiver type. + * If the receiver does not contain the value it returns the original enumerable. + */ + without(value: T): NativeArray; + /** + * Returns a new enumerable that contains only unique values. The default + * implementation returns an array regardless of the receiver type. + */ + uniq(): NativeArray; + /** + * Converts the enumerable into an array and sorts by the keys + * specified in the argument. + */ + sortBy(property: string): NativeArray; + /** + * Returns a new enumerable that contains only items containing a unique property value. + * The default implementation returns an array regardless of the receiver type. + */ + uniqBy(property: string): NativeArray; + /** + * Returns `true` if the passed object can be found in the enumerable. + */ + includes(searchElement: T, fromIndex?: number): boolean; + /** + * This is the handler for the special array content property. If you get + * this property, it will return this. If you set this property to a new + * array, it will replace the current content. + */ + '[]': ComputedProperty; + } + const Enumerable: Mixin>; + /** + A subclass of the JavaScript Error object for use in Ember. + **/ + const Error: ErrorConstructor; + /** + * `Ember.EventDispatcher` handles delegating browser events to their + * corresponding `Ember.Views.` For example, when you click on a view, + * `Ember.EventDispatcher` ensures that that view's `mouseDown` method gets + * called. + */ + class EventDispatcher extends Object { + /** + * The set of events names (and associated handler function names) to be setup + * and dispatched by the `EventDispatcher`. Modifications to this list can be done + * at setup time, generally via the `Ember.Application.customEvents` hash. + */ + events: EventDispatcherEvents; + } + /** + * This mixin allows for Ember objects to subscribe to and emit events. + */ + interface Evented { + /** + * Subscribes to a named event with given function. + */ + on( + name: string, + target: Target, + method: (this: Target, ...args: any[]) => void + ): this; + on(name: string, method: (...args: any[]) => void): this; + /** + * Subscribes a function to a named event and then cancels the subscription + * after the first time the event is triggered. It is good to use ``one`` when + * you only care about the first time an event has taken place. + */ + one( + name: string, + target: Target, + method: (this: Target, ...args: any[]) => void + ): this; + one(name: string, method: (...args: any[]) => void): this; + /** + * Triggers a named event for the object. Any additional arguments + * will be passed as parameters to the functions that are subscribed to the + * event. + */ + trigger(name: string, ...args: any[]): any; + /** + * Cancels subscription for given name, target, and method. + */ + off( + name: string, + target: Target, + method: (this: Target, ...args: any[]) => void + ): this; + off(name: string, method: (...args: any[]) => void): this; + /** + * Checks to see if object has any subscriptions for named event. + */ + has(name: string): boolean; + } + const Evented: Mixin; + /** + * The `Ember.Freezable` mixin implements some basic methods for marking an + * object as frozen. Once an object is frozen it should be read only. No changes + * may be made the internal state of the object. + * @deprecated Use `Object.freeze` instead. + */ + interface Freezable { + freeze(): Freezable; + isFrozen: boolean; + } + const Freezable: Mixin; + /** + * `Ember.HashLocation` implements the location API using the browser's + * hash. At present, it relies on a `hashchange` event existing in the + * browser. + */ + class HashLocation extends Object {} + /** + * Ember.HistoryLocation implements the location API using the browser's + * history.pushState API. + */ + class HistoryLocation extends Object {} + /** + * Ember Helpers are functions that can compute values, and are used in templates. + * For example, this code calls a helper named `format-currency`: + */ + class Helper extends Object { + /** + * In many cases, the ceremony of a full `Ember.Helper` class is not required. + * The `helper` method create pure-function helpers without instances. For + * example: + */ + static helper(helper: (params: any[], hash?: object) => any): Helper; + /** + * Override this function when writing a class-based helper. + */ + compute(params: any[], hash: object): any; + /** + * On a class-based helper, it may be useful to force a recomputation of that + * helpers value. This is akin to `rerender` on a component. + */ + recompute(): any; + } + /** + * The purpose of the Ember Instrumentation module is + * to provide efficient, general-purpose instrumentation + * for Ember. + */ + const Instrumentation: { + instrument(name: string, payload: any, callback: Function, binding: any): void; + reset(): void; + subscribe(pattern: string, object: any): void; + unsubscribe(subscriber: any): void; + }; + /** + * `Ember.LinkComponent` renders an element whose `click` event triggers a + * transition of the application's instance of `Ember.Router` to + * a supplied route by name. + */ + class LinkComponent extends Component { + /** + * Used to determine when this `LinkComponent` is active. + */ + currentWhen: any; + /** + * Sets the `title` attribute of the `LinkComponent`'s HTML element. + */ + title: string | null; + /** + * Sets the `rel` attribute of the `LinkComponent`'s HTML element. + */ + rel: string | null; + /** + * Sets the `tabindex` attribute of the `LinkComponent`'s HTML element. + */ + tabindex: string | null; + /** + * Sets the `target` attribute of the `LinkComponent`'s HTML element. + */ + target: string | null; + /** + * The CSS class to apply to `LinkComponent`'s element when its `active` + * property is `true`. + */ + activeClass: string; + /** + * Determines whether the `LinkComponent` will trigger routing via + * the `replaceWith` routing strategy. + */ + replace: boolean; + } + /** + * Ember.Location returns an instance of the correct implementation of + * the `location` API. + */ + const Location: { + /** + * This is deprecated in favor of using the container to lookup the location + * implementation as desired. + * @deprecated Use the container to lookup the location implementation that you need. + */ + create(options?: {}): any; + }; + /** + * Inside Ember-Metal, simply uses the methods from `imports.console`. + * Override this to provide more robust logging functionality. + */ + const Logger: { + /** + * If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. + */ + assert(test: boolean, message?: string): void; + /** + * Logs the arguments to the console in blue text. + */ + debug(...args: any[]): void; + /** + * Prints the arguments to the console with an error icon, red text and a stack trace. + */ + error(...args: any[]): void; + /** + * Logs the arguments to the console. + */ + info(...args: any[]): void; + /** + * Logs the arguments to the console. + */ + log(...args: any[]): void; + /** + * Prints the arguments to the console with a warning icon. + */ + warn(...args: any[]): void; + }; + /** + * A Map stores values indexed by keys. Unlike JavaScript's + * default Objects, the keys of a Map can be any JavaScript + * object. + * @deprecated + */ + class Map { + copy(): Map; + static create(): Map; + forEach(callback: Function, self: any): void; + get(key: any): any; + has(key: any): boolean; + set(key: any, value: any): void; + length: number; + } + /** + * @deprecated + */ + class MapWithDefault extends Map { + copy(): MapWithDefault; + static create(): MapWithDefault; + } + /** + * The `Ember.Mixin` class allows you to create mixins, whose properties can be + * added to other classes. + */ + class Mixin { + /** + * Mixin needs to have *something* on its prototype, otherwise it's treated like an empty interface. + * It cannot be private, sadly. + */ + __ember_mixin__: never; + + static create( + args?: MixinOrLiteral & ThisType> + ): Mixin; + + static create( + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType> + ): Mixin; + + static create( + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType>, + arg3: MixinOrLiteral & ThisType> + ): Mixin; + + static create( + arg1: MixinOrLiteral & ThisType>, + arg2: MixinOrLiteral & ThisType>, + arg3: MixinOrLiteral & ThisType>, + arg4: MixinOrLiteral & ThisType> + ): Mixin; + } + /** + * This mixin defines the API for modifying array-like objects. These methods + * can be applied only to a collection that keeps its items in an ordered set. + * It builds upon the Array mixin and adds methods to modify the array. + * One concrete implementations of this class include ArrayProxy. + */ + interface MutableArray extends Array, MutableEnumerable { + /** + * __Required.__ You must implement this method to apply this mixin. + */ + replace(idx: number, amt: number, objects: any[]): any; + /** + * Remove all elements from the array. This is useful if you + * want to reuse an existing array without having to recreate it. + */ + clear(): this; + /** + * This will use the primitive `replace()` method to insert an object at the + * specified index. + */ + insertAt(idx: number, object: {}): this; + /** + * Remove an object at the specified index using the `replace()` primitive + * method. You can pass either a single index, or a start and a length. + */ + removeAt(start: number, len?: number): this; + /** + * Push the object onto the end of the array. Works just like `push()` but it + * is KVO-compliant. + */ + pushObject(obj: T): T; + /** + * Add the objects in the passed numerable to the end of the array. Defers + * notifying observers of the change until all objects are added. + */ + pushObjects(objects: Enumerable): this; + /** + * Pop object from array or nil if none are left. Works just like `pop()` but + * it is KVO-compliant. + */ + popObject(): T; + /** + * Shift an object from start of array or nil if none are left. Works just + * like `shift()` but it is KVO-compliant. + */ + shiftObject(): T; + /** + * Unshift an object to start of array. Works just like `unshift()` but it is + * KVO-compliant. + */ + unshiftObject(obj: T): T; + /** + * Adds the named objects to the beginning of the array. Defers notifying + * observers until all objects have been added. + */ + unshiftObjects(objects: Enumerable): this; + /** + * Reverse objects in the array. Works just like `reverse()` but it is + * KVO-compliant. + */ + reverseObjects(): this; + /** + * Replace all the receiver's content with content of the argument. + * If argument is an empty array receiver will be cleared. + */ + setObjects(objects: Ember.Array): this; + } + const MutableArray: Mixin>; + /** + * This mixin defines the API for modifying generic enumerables. These methods + * can be applied to an object regardless of whether it is ordered or + * unordered. + */ + interface MutableEnumerable extends Enumerable { + /** + * __Required.__ You must implement this method to apply this mixin. + */ + addObject(object: T): T; + /** + * Adds each object in the passed enumerable to the receiver. + */ + addObjects(objects: Enumerable): this; + /** + * __Required.__ You must implement this method to apply this mixin. + */ + removeObject(object: T): T; + /** + * Removes each object in the passed enumerable from the receiver. + */ + removeObjects(objects: Enumerable): this; + } + const MutableEnumerable: Mixin>; + /** + * A Namespace is an object usually used to contain other objects or methods + * such as an application or framework. Create a namespace anytime you want + * to define one of these new containers. + */ + class Namespace extends Object {} + /** + * The NativeArray mixin contains the properties needed to make the native + * Array support Ember.MutableArray and all of its dependent APIs. Unless you + * have `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Array` set to + * false, this will be applied automatically. Otherwise you can apply the mixin + * at anytime by calling `Ember.NativeArray.apply(Array.prototype)`. + */ + interface NativeArray extends GlobalArray, MutableArray, Observable, Copyable { + /** + * __Required.__ You must implement this method to apply this mixin. + */ + length: number; + } + const NativeArray: Mixin>; + /** + * Ember.NoneLocation does not interact with the browser. It is useful for + * testing, or when you need to manage state with your Router, but temporarily + * don't want it to muck with the URL (for example when you embed your + * application in a larger page). + */ + class NoneLocation extends Object {} + /** + * `Ember.Object` is the main base class for all Ember objects. It is a subclass + * of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details, + * see the documentation for each of these. + */ + class Object extends CoreObject.extend(Observable) {} + /** + * `Ember.ObjectProxy` forwards all properties not defined by the proxy itself + * to a proxied `content` object. + */ + class ObjectProxy extends Object { + /** + The object whose properties will be forwarded. + **/ + content: Object; + } + /** + * This mixin provides properties and property observing functionality, core features of the Ember object model. + */ + interface Observable { + /** + * Retrieves the value of a property from the object. + */ + get(this: ComputedPropertyGetters, key: K): T[K]; + /** + * To get the values of multiple properties at once, call `getProperties` + * with a list of strings or an array: + */ + getProperties(this: ComputedPropertyGetters, list: K[]): Pick; + getProperties( + this: ComputedPropertyGetters, + ...list: K[] + ): Pick; + /** + * Sets the provided key or path to the value. + */ + set(this: ComputedPropertySetters, key: K, value: T[K]): T[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( + this: ComputedPropertySetters, + hash: Pick + ): Pick; + /** + * Convenience method to call `propertyWillChange` and `propertyDidChange` in + * succession. + */ + notifyPropertyChange(keyName: string): this; + /** + * Adds an observer on a property. + */ + addObserver( + key: keyof this, + target: Target, + method: ObserverMethod + ): void; + addObserver( + key: keyof this, + method: ObserverMethod + ): void; + /** + * Remove an observer you have previously registered on this object. Pass + * the same key, target, and method you passed to `addObserver()` and your + * target will no longer receive notifications. + */ + removeObserver( + key: keyof this, + target: Target, + method: ObserverMethod + ): any; + removeObserver( + key: keyof this, + method: ObserverMethod + ): any; + /** + * Retrieves the value of a property, or a default value in the case that the + * property returns `undefined`. + */ + getWithDefault( + this: ComputedPropertyGetters, + key: K, + defaultValue: T[K] + ): T[K]; + /** + * Set the value of a property to the current value plus some amount. + */ + incrementProperty(keyName: keyof this, increment?: number): number; + /** + * Set the value of a property to the current value minus some amount. + */ + decrementProperty(keyName: keyof this, decrement?: number): number; + /** + * Set the value of a boolean property to the opposite of its + * current value. + */ + toggleProperty(keyName: keyof this): boolean; + /** + * Returns the cached value of a computed property, if it exists. + * This allows you to inspect the value of a computed property + * without accidentally invoking it if it is intended to be + * generated lazily. + */ + cacheFor(this: ComputedPropertyGetters, key: K): T[K] | undefined; + } + const Observable: Mixin; + /** + * This class is used internally by Ember and Ember Data. + * Please do not use it at this time. We plan to clean it up + * and add many tests soon. + * @deprecated + */ + class OrderedSet { + add(obj: any): void; + clear(): void; + copy(): OrderedSet; + static create(): OrderedSet; + forEach(fn: Function, self: any): void; + has(obj: any): boolean; + isEmpty(): boolean; + toArray(): any[]; + } + /** + * A low level mixin making ObjectProxy promise-aware. + */ + interface PromiseProxyMixin extends RSVP.Promise { + /** + * If the proxied promise is rejected this will contain the reason + * provided. + */ + reason: any; + /** + * Once the proxied promise has settled this will become `false`. + */ + isPending: boolean; + /** + * Once the proxied promise has settled this will become `true`. + */ + isSettled: boolean; + /** + * Will become `true` if the proxied promise is rejected. + */ + isRejected: boolean; + /** + * Will become `true` if the proxied promise is fulfilled. + */ + isFulfilled: boolean; + /** + * The promise whose fulfillment value is being proxied by this object. + */ + promise: RSVP.Promise; + } + const PromiseProxyMixin: Mixin>; + /** + * A registry used to store factory and option information keyed + * by type. + */ + class Registry { + register( + fullName: string, + factory: EmberClassConstructor, + options?: { singleton?: boolean } + ): void; + } + class Resolver extends Ember.Object {} + /** + The `Ember.Route` class is used to define individual routes. Refer to + the [routing guide](http://emberjs.com/guides/routing/) for documentation. + */ + class Route extends Object.extend(ActionHandler, Evented) { + // methods + /** + This hook is called after this route's model has resolved. + It follows identical async/promise semantics to `beforeModel` + but is provided the route's resolved model in addition to + the `transition`, and is therefore suited to performing + logic that can only take place after the model has already + resolved. + */ + afterModel(resolvedModel: any, transition: Transition): any; + + /** + This hook is the first of the route entry validation hooks + called when an attempt is made to transition into a route + or one of its children. It is called before `model` and + `afterModel`, and is appropriate for cases when: + 1) A decision can be made to redirect elsewhere without + needing to resolve the model first. + 2) Any async operations need to occur first before the + model is attempted to be resolved. + This hook is provided the current `transition` attempt + as a parameter, which can be used to `.abort()` the transition, + save it for a later `.retry()`, or retrieve values set + on it from a previous hook. You can also just call + `this.transitionTo` to another route to implicitly + abort the `transition`. + You can return a promise from this hook to pause the + transition until the promise resolves (or rejects). This could + be useful, for instance, for retrieving async code from + the server that is required to enter a route. + */ + beforeModel(transition: Transition): any; + + /** + * Returns the controller for a particular route or name. + * The controller instance must already have been created, either through entering the + * associated route or using `generateController`. + */ + controllerFor(name: K): ControllerRegistry[K]; + + /** + * Disconnects a view that has been rendered into an outlet. + */ + disconnectOutlet(options: string | { outlet?: string; parentView?: string }): void; + + /** + * A hook you can implement to convert the URL into the model for + * this route. + */ + model(params: {}, transition: Transition): any; + + /** + * Returns the model of a parent (or any ancestor) route + * in a route hierarchy. During a transition, all routes + * must resolve a model object, and if a route + * needs access to a parent route's model in order to + * resolve a model (or just reuse the model from a parent), + * it can call `this.modelFor(theNameOfParentRoute)` to + * retrieve it. + */ + modelFor(name: string): {}; + + /** + * Retrieves parameters, for current route using the state.params + * variable and getQueryParamsFor, using the supplied routeName. + */ + paramsFor(name: string): {}; + + /** + * Refresh the model on this route and any child routes, firing the + * `beforeModel`, `model`, and `afterModel` hooks in a similar fashion + * to how routes are entered when transitioning in from other route. + * The current route params (e.g. `article_id`) will be passed in + * to the respective model hooks, and if a different model is returned, + * `setupController` and associated route hooks will re-fire as well. + * An example usage of this method is re-querying the server for the + * latest information using the same parameters as when the route + * was first entered. + * Note that this will cause `model` hooks to fire even on routes + * that were provided a model object when the route was initially + * entered. + */ + redirect(): Transition; + + /** + * Refresh the model on this route and any child routes, firing the + * `beforeModel`, `model`, and `afterModel` hooks in a similar fashion + * to how routes are entered when transitioning in from other route. + * The current route params (e.g. `article_id`) will be passed in + * to the respective model hooks, and if a different model is returned, + * `setupController` and associated route hooks will re-fire as well. + * An example usage of this method is re-querying the server for the + * latest information using the same parameters as when the route + * was first entered. + * Note that this will cause `model` hooks to fire even on routes + * that were provided a model object when the route was initially + * entered. + */ + refresh(): Transition; + + /** + * `render` is used to render a template into a region of another template + * (indicated by an `{{outlet}}`). `render` is used both during the entry + * phase of routing (via the `renderTemplate` hook) and later in response to + * user interaction. + */ + render(name: string, options?: RenderOptions): void; + + /** + * A hook you can use to render the template for the current route. + * This method is called with the controller for the current route and the + * model supplied by the `model` hook. By default, it renders the route's + * template, configured with the controller for the route. + * This method can be overridden to set up and render additional or + * alternative templates. + */ + renderTemplate(controller: Controller, model: {}): void; + + /** + * Transition into another route while replacing the current URL, if possible. + * This will replace the current history entry instead of adding a new one. + * Beside that, it is identical to `transitionTo` in all other respects. See + * 'transitionTo' for additional information regarding multiple models. + */ + replaceWith(name: string, ...args: any[]): Transition; + + /** + * A hook you can use to reset controller values either when the model + * changes or the route is exiting. + */ + resetController(controller: Controller, isExiting: boolean, transition: any): void; + + /** + * Sends an action to the router, which will delegate it to the currently active + * route hierarchy per the bubbling rules explained under actions. + */ + send(name: string, ...args: any[]): void; + + /** + * A hook you can implement to convert the route's model into parameters + * for the URL. + * + * The default `serialize` method will insert the model's `id` into the + * route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'. + * If the route has multiple dynamic segments or does not contain '_id', `serialize` + * will return `Ember.getProperties(model, params)` + * This method is called when `transitionTo` is called with a context + * in order to populate the URL. + */ + serialize(model: {}, params: string[]): string | object; + + /** + * A hook you can use to setup the controller for the current route. + * This method is called with the controller for the current route and the + * model supplied by the `model` hook. + * By default, the `setupController` hook sets the `model` property of + * the controller to the `model`. + * If you implement the `setupController` hook in your Route, it will + * prevent this default behavior. If you want to preserve that behavior + * when implementing your `setupController` function, make sure to call + * `_super` + */ + setupController(controller: Controller, model: {}): void; + + /** + * Transition the application into another route. The route may + * be either a single route or route path + */ + transitionTo(name: string, ...object: any[]): Transition; + + /** + * The name of the view to use by default when rendering this routes template. + * When rendering a template, the route will, by default, determine the + * template and view to use from the name of the route itself. If you need to + * define a specific view, set this property. + * This is useful when multiple routes would benefit from using the same view + * because it doesn't require a custom `renderTemplate` method. + */ + transitionTo(name: string, ...object: any[]): Transition; + + // https://emberjs.com/api/ember/3.2/classes/Route/methods/intermediateTransitionTo?anchor=intermediateTransitionTo + /** + * Perform a synchronous transition into another route without attempting to resolve promises, + * update the URL, or abort any currently active asynchronous transitions + * (i.e. regular transitions caused by transitionTo or URL changes). + * + * @param name the name of the route or a URL + * @param object the model(s) or identifier(s) to be used while + * transitioning to the route. + * @returns the Transition object associated with this attempted transition + */ + intermediateTransitionTo(name: string, ...object: any[]): Transition; + + // properties + /** + * The controller associated with this route. + */ + controller: Controller; + + /** + * The name of the controller to associate with this route. + * By default, Ember will lookup a route's controller that matches the name + * of the route (i.e. `App.PostController` for `App.PostRoute`). However, + * if you would like to define a specific controller to use, you can do so + * using this property. + * This is useful in many ways, as the controller specified will be: + * * p assed to the `setupController` method. + * * used as the controller for the view being rendered by the route. + * * returned from a call to `controllerFor` for the route. + */ + controllerName: string; + + /** + * Configuration hash for this route's queryParams. + */ + queryParams: { [key: string]: RouteQueryParam }; + + /** + * The name of the route, dot-delimited + */ + routeName: string; + + /** + * The name of the template to use by default when rendering this routes + * template. + * This is similar with `viewName`, but is useful when you just want a custom + * template without a view. + */ + templateName: string; + + // events + /** + * This hook is executed when the router enters the route. It is not executed + * when the model for the route changes. + */ + activate(): void; + + /** + * This hook is executed when the router completely exits this route. It is + * not executed when the model for the route changes. + */ + deactivate(): void; + + /** + * The didTransition action is fired after a transition has successfully been + * completed. This occurs after the normal model hooks (beforeModel, model, + * afterModel, setupController) have resolved. The didTransition action has + * no arguments, however, it can be useful for tracking page views or resetting + * state on the controller. + */ + didTransition(): void; + + /** + * When attempting to transition into a route, any of the hooks may return a promise + * that rejects, at which point an error action will be fired on the partially-entered + * routes, allowing for per-route error handling logic, or shared error handling logic + * defined on a parent route. + */ + error(error: any, transition: Transition): void; + + /** + * The loading action is fired on the route when a route's model hook returns a + * promise that is not already resolved. The current Transition object is the first + * parameter and the route that triggered the loading event is the second parameter. + */ + loading(transition: Transition, route: Route): void; + + /** + * The willTransition action is fired at the beginning of any attempted transition + * with a Transition object as the sole argument. This action can be used for aborting, + * redirecting, or decorating the transition from the currently active routes. + */ + willTransition(transition: Transition): void; + } + /** + * The `Ember.Router` class manages the application state and URLs. Refer to + * the [routing guide](http://emberjs.com/guides/routing/) for documentation. + */ + class Router extends Object.extend(Evented) { + /** + * The `Router.map` function allows you to define mappings from URLs to routes + * in your application. These mappings are defined within the + * supplied callback function using `this.route`. + */ + static map(callback: (this: RouterDSL) => void): void; + /** + * The `location` property determines the type of URL's that your + * application will use. + */ + location: string; + /** + * Represents the URL of the root of the application, often '/'. This prefix is + * assumed on all routes defined on this router. + */ + rootURL: string; + /** + * Handles updating the paths and notifying any listeners of the URL + * change. + */ + didTransition(): any; + /** + * Handles notifying any listeners of an impending URL + * change. + */ + willTransition(): any; + /** + * Transition the application into another route. The route may + * be either a single route or route path: + */ + transitionTo(name: string, options?: {}): Transition; + transitionTo(name: string, ...models: any[]): Transition; + transitionTo(name: string, options: {}): Transition; + } + class RouterDSL { + constructor(name: string, options: object); + route(name: string, callback: (this: RouterDSL) => void): void; + route( + name: string, + options?: { path?: string; resetNamespace?: boolean }, + callback?: (this: RouterDSL) => void + ): void; + mount( + name: string, + options?: { + as?: string, + path?: string, + resetNamespace?: boolean, + engineInfo?: any + } + ): void; + } + class Service extends Object {} + /** + * The internal class used to create textarea element when the `{{textarea}}` + * helper is used. + */ + class TextArea extends Component.extend(TextSupport) {} + /** + * The internal class used to create text inputs when the `{{input}}` + * helper is used with `type` of `text`. + */ + class TextField extends Component.extend(TextSupport) { + /** + * The `value` attribute of the input element. As the user inputs text, this + * property is updated live. + */ + value: string; + /** + * The `type` attribute of the input element. + */ + type: string; + /** + * The `size` of the text field in characters. + */ + size: string; + /** + * The `pattern` attribute of input element. + */ + pattern: string; + /** + * The `min` attribute of input element used with `type="number"` or `type="range"`. + */ + min: string; + /** + * The `max` attribute of input element used with `type="number"` or `type="range"`. + */ + max: string; + } + /** + * `TextSupport` is a shared mixin used by both `Ember.TextField` and + * `Ember.TextArea`. `TextSupport` adds a number of methods that allow you to + * specify a controller action to invoke when a certain event is fired on your + * text field or textarea. The specifed controller action would get the current + * value of the field passed in as the only argument unless the value of + * the field is empty. In that case, the instance of the field itself is passed + * in as the only argument. + */ + interface TextSupport extends TargetActionSupport { + cancel(event: Function): void; + focusIn(event: Function): void; + focusOut(event: Function): void; + insertNewLine(event: Function): void; + keyPress(event: Function): void; + action: string; + bubbles: boolean; + onEvent: string; + } + const TextSupport: Ember.Mixin; + interface Transition { + /** + Aborts the Transition. Note you can also implicitly abort a transition + by initiating another transition while a previous one is underway. + */ + abort(): Transition; + /** + Retries a previously-aborted transition (making sure to abort the + transition if it's still active). Returns a new transition that + represents the new attempt to transition. + */ + retry(): Transition; + } + interface ViewTargetActionSupport { + target: any; + actionContext: any; + } + const ViewTargetActionSupport: Mixin; + const ViewUtils: { + isSimpleClick(event: Event): boolean; + }; + + // FYI - RSVP source comes from https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/promise.js + const RSVP: typeof Rsvp; + namespace RSVP { + type Promise = Rsvp.Promise; + } + + /** + * This is a container for an assortment of testing related functionality + */ + namespace Test { + /** + * `registerHelper` is used to register a test helper that will be injected + * when `App.injectTestHelpers` is called. + */ + function registerHelper( + name: string, + helperMethod: (app: Application, ...args: any[]) => any, + options?: object + ): any; + /** + * `registerAsyncHelper` is used to register an async test helper that will be injected + * when `App.injectTestHelpers` is called. + */ + function registerAsyncHelper( + name: string, + helperMethod: (app: Application, ...args: any[]) => any + ): void; + /** + * Remove a previously added helper method. + */ + function unregisterHelper(name: string): void; + /** + * Used to register callbacks to be fired whenever `App.injectTestHelpers` + * is called. + */ + function onInjectHelpers(callback: (app: Application) => void): void; + /** + * This returns a thenable tailored for testing. It catches failed + * `onSuccess` callbacks and invokes the `Ember.Test.adapter.exception` + * callback in the last chained then. + */ + function promise( + resolver: ( + resolve: (value?: T | PromiseLike) => void, + reject: (reason?: any) => void + ) => void, + label?: string + ): Ember.Test.Promise; + /** + * Replacement for `Ember.RSVP.resolve` + * The only difference is this uses + * an instance of `Ember.Test.Promise` + */ + function resolve(value?: T | PromiseLike, label?: string): Ember.Test.Promise; + /** + * This allows ember-testing to play nicely with other asynchronous + * events, such as an application that is waiting for a CSS3 + * transition or an IndexDB transaction. The waiter runs periodically + * after each async helper (i.e. `click`, `andThen`, `visit`, etc) has executed, + * until the returning result is truthy. After the waiters finish, the next async helper + * is executed and the process repeats. + */ + function registerWaiter(callback: () => boolean): any; + function registerWaiter( + context: Context, + callback: (this: Context) => boolean + ): any; + /** + * `unregisterWaiter` is used to unregister a callback that was + * registered with `registerWaiter`. + */ + function unregisterWaiter(callback: () => boolean): any; + function unregisterWaiter( + context: Context, + callback: (this: Context) => boolean + ): any; + /** + * Iterates through each registered test waiter, and invokes + * its callback. If any waiter returns false, this method will return + * true indicating that the waiters have not settled yet. + */ + function checkWaiters(): boolean; + /** + * Used to allow ember-testing to communicate with a specific testing + * framework. + */ + const adapter: Adapter; + /** + * The primary purpose of this class is to create hooks that can be implemented + * by an adapter for various test frameworks. + */ + class Adapter { + /** + * This callback will be called whenever an async operation is about to start. + */ + asyncStart(): any; + /** + * This callback will be called whenever an async operation has completed. + */ + asyncEnd(): any; + /** + * Override this method with your testing framework's false assertion. + * This function is called whenever an exception occurs causing the testing + * promise to fail. + */ + exception(error: string): any; + } + /** + * This class implements the methods defined by Ember.Test.Adapter for the + * QUnit testing framework. + */ + class QUnitAdapter extends Adapter {} + class Promise extends Rsvp.Promise { + constructor( + executor: ( + resolve: (value?: T | PromiseLike) => void, + reject: (reason?: any) => void + ) => void + ); + } + } + /** + * Namespace for injection helper methods. + */ + namespace inject { + /** + * Creates a property that lazily looks up another controller in the container. + * Can only be used when defining another controller. + */ + function controller(): ComputedProperty; + function controller( + name: K + ): ComputedProperty; + /** + * Creates a property that lazily looks up a service in the container. There + * are no restrictions as to what objects a service can be injected into. + */ + function service(): ComputedProperty; + function service( + name: K + ): ComputedProperty; + } + namespace ENV { + const EXTEND_PROTOTYPES: typeof Ember.EXTEND_PROTOTYPES; + const LOG_BINDINGS: boolean; + const LOG_STACKTRACE_ON_DEPRECATION: boolean; + const LOG_VERSION: boolean; + const MODEL_FACTORY_INJECTIONS: boolean; + const RAISE_ON_DEPRECATION: boolean; + } + namespace EXTEND_PROTOTYPES { + const Array: boolean; + const Function: boolean; + const String: boolean; + } + namespace Handlebars { + function compile(string: string): Function; + function compile(environment: any, options?: any, context?: any, asObject?: any): any; + function precompile(string: string, options: any): void; + class Compiler {} + class JavaScriptCompiler {} + function registerPartial(name: string, str: any): void; + function K(): any; + function createFrame(objec: any): any; + function Exception(message: string): void; + class SafeString { + constructor(str: string); + toString(): string; + } + function parse(string: string): any; + function print(ast: any): void; + const logger: typeof Ember.Logger; + function log(level: string, str: string): void; + } + namespace String { + function camelize(str: string): string; + function capitalize(str: string): string; + function classify(str: string): string; + function dasherize(str: string): string; + function decamelize(str: string): string; + function fmt(...args: string[]): string; + function htmlSafe(str: string): void; // TODO: @returns Handlebars.SafeStringStatic; + function isHTMLSafe(str: string): boolean; + function loc(...args: string[]): string; + function underscore(str: string): string; + function w(str: string): string[]; + } + const computed: { + (cb: ComputedPropertyCallback): ComputedProperty; + (k1: string, cb: ComputedPropertyCallback): ComputedProperty; + (k1: string, k2: string, cb: ComputedPropertyCallback): ComputedProperty; + ( + k1: string, + k2: string, + k3: string, + cb: ComputedPropertyCallback + ): ComputedProperty; + ( + k1: string, + k2: string, + k3: string, + k4: string, + cb: ComputedPropertyCallback + ): ComputedProperty; + ( + k1: string, + k2: string, + k3: string, + k4: string, + k5: string, + cb: ComputedPropertyCallback + ): ComputedProperty; + ( + k1: string, + k2: string, + k3: string, + k4: string, + k5: string, + k6: string, + cb: ComputedPropertyCallback + ): ComputedProperty; + ( + k1: string, + k2: string, + k3: string, + k4: string, + k5: string, + k6: string, + k7: string, + ...rest: any[] + ): ComputedProperty; + + /** + * A computed property that returns true if the value of the dependent + * property is null, an empty string, empty array, or empty function. + */ + empty(dependentKey: string): ComputedProperty; + /** + * A computed property that returns true if the value of the dependent + * property is NOT null, an empty string, empty array, or empty function. + */ + notEmpty(dependentKey: string): ComputedProperty; + /** + * A computed property that returns true if the value of the dependent + * property is null or undefined. This avoids errors from JSLint complaining + * about use of ==, which can be technically confusing. + */ + none(dependentKey: string): ComputedProperty; + /** + * A computed property that returns the inverse boolean value + * of the original value for the dependent property. + */ + not(dependentKey: string): ComputedProperty; + /** + * A computed property that converts the provided dependent property + * into a boolean value. + */ + bool(dependentKey: string): ComputedProperty; + /** + * A computed property which matches the original value for the + * dependent property against a given RegExp, returning `true` + * if the value matches the RegExp and `false` if it does not. + */ + match(dependentKey: string, regexp: RegExp): ComputedProperty; + /** + * A computed property that returns true if the provided dependent property + * is equal to the given value. + */ + equal(dependentKey: string, value: any): ComputedProperty; + /** + * A computed property that returns true if the provided dependent property + * is greater than the provided value. + */ + gt(dependentKey: string, value: number): ComputedProperty; + /** + * A computed property that returns true if the provided dependent property + * is greater than or equal to the provided value. + */ + gte(dependentKey: string, value: number): ComputedProperty; + /** + * A computed property that returns true if the provided dependent property + * is less than the provided value. + */ + lt(dependentKey: string, value: number): ComputedProperty; + /** + * A computed property that returns true if the provided dependent property + * is less than or equal to the provided value. + */ + lte(dependentKey: string, value: number): ComputedProperty; + /** + * A computed property that performs a logical `and` on the + * original values for the provided dependent properties. + */ + and(...dependentKeys: string[]): ComputedProperty; + /** + * A computed property which performs a logical `or` on the + * original values for the provided dependent properties. + */ + or(...dependentKeys: string[]): ComputedProperty; + /** + * Creates a new property that is an alias for another property + * on an object. Calls to `get` or `set` this property behave as + * though they were called on the original property. + */ + alias(dependentKey: string): ComputedProperty; + /** + * Where `computed.alias` aliases `get` and `set`, and allows for bidirectional + * data flow, `computed.oneWay` only provides an aliased `get`. The `set` will + * not mutate the upstream property, rather causes the current property to + * become the value set. This causes the downstream property to permanently + * diverge from the upstream property. + */ + oneWay(dependentKey: string): ComputedProperty; + /** + * This is a more semantically meaningful alias of `computed.oneWay`, + * whose name is somewhat ambiguous as to which direction the data flows. + */ + reads(dependentKey: string): ComputedProperty; + /** + * Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides + * a readOnly one way binding. Very often when using `computed.oneWay` one does + * not also want changes to propagate back up, as they will replace the value. + */ + readOnly(dependentKey: string): ComputedProperty; + /** + * Creates a new property that is an alias for another property + * on an object. Calls to `get` or `set` this property behave as + * though they were called on the original property, but also + * print a deprecation warning. + */ + deprecatingAlias( + dependentKey: string, + options: { id: string; until: string } + ): ComputedProperty; + /** + * @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options + */ + deprecatingAlias( + dependentKey: string, + options?: { id?: string; until?: string } + ): ComputedProperty; + /** + * A computed property that returns the sum of the values + * in the dependent array. + */ + sum(dependentKey: string): ComputedProperty; + /** + * A computed property that calculates the maximum value in the + * dependent array. This will return `-Infinity` when the dependent + * array is empty. + */ + max(dependentKey: string): ComputedProperty; + /** + * A computed property that calculates the minimum value in the + * dependent array. This will return `Infinity` when the dependent + * array is empty. + */ + min(dependentKey: string): ComputedProperty; + /** + * Returns an array mapped via the callback + */ + map( + dependentKey: string, + callback: (value: any, index: number, array: any[]) => U + ): ComputedProperty; + /** + * Returns an array mapped to the specified key. + */ + mapBy(dependentKey: string, propertyKey: string): ComputedProperty; + /** + * Filters the array by the callback. + */ + filter( + dependentKey: string, + callback: (value: any, index: number, array: any[]) => boolean + ): ComputedProperty; + /** + * Filters the array by the property and value + */ + filterBy( + dependentKey: string, + propertyKey: string, + value?: any + ): ComputedProperty; + /** + * A computed property which returns a new array with all the unique + * elements from one or more dependent arrays. + */ + uniq(propertyKey: string): ComputedProperty; + /** + * A computed property which returns a new array with all the unique + * elements from an array, with uniqueness determined by specific key. + */ + uniqBy(dependentKey: string, propertyKey: string): ComputedProperty; + /** + * A computed property which returns a new array with all the unique + * elements from one or more dependent arrays. + */ + union(...propertyKeys: string[]): ComputedProperty; + /** + * A computed property which returns a new array with all the elements + * two or more dependent arrays have in common. + */ + intersect(...propertyKeys: string[]): ComputedProperty; + /** + * A computed property which returns a new array with all the + * properties from the first dependent array that are not in the second + * dependent array. + */ + setDiff(setAProperty: string, setBProperty: string): ComputedProperty; + /** + * A computed property that returns the array of values + * for the provided dependent properties. + */ + collect(...dependentKeys: string[]): ComputedProperty; + /** + * A computed property which returns a new array with all the + * properties from the first dependent array sorted based on a property + * or sort function. + */ + sort( + itemsKey: string, + sortDefinition: string | ((itemA: any, itemB: any) => number) + ): ComputedProperty; + }; + const run: { + /** + * Runs the passed target and method inside of a RunLoop, ensuring any + * deferred actions including bindings and views updates are flushed at the + * end. + */ + (method: (...args: any[]) => Ret): Ret; + (target: Target, method: RunMethod): Ret; + /** + * If no run-loop is present, it creates a new one. If a run loop is + * present it will queue itself to run on the existing run-loops action + * queue. + */ + join(method: (...args: any[]) => Ret, ...args: any[]): Ret | undefined; + join( + target: Target, + method: RunMethod, + ...args: any[] + ): Ret | undefined; + /** + * Allows you to specify which context to call the specified function in while + * adding the execution of that function to the Ember run loop. This ability + * makes this method a great way to asynchronously integrate third-party libraries + * into your Ember application. + */ + bind( + target: Target, + method: RunMethod, + ...args: any[] + ): (...args: any[]) => Ret; + /** + * Begins a new RunLoop. Any deferred actions invoked after the begin will + * be buffered until you invoke a matching call to `run.end()`. This is + * a lower-level way to use a RunLoop instead of using `run()`. + */ + begin(): void; + /** + * Ends a RunLoop. This must be called sometime after you call + * `run.begin()` to flush any deferred actions. This is a lower-level way + * to use a RunLoop instead of using `run()`. + */ + end(): void; + /** + * Adds the passed target/method and any optional arguments to the named + * queue to be executed at the end of the RunLoop. If you have not already + * started a RunLoop when calling this method one will be started for you + * automatically. + */ + schedule( + queue: EmberRunQueues, + target: Target, + method: RunMethod, + ...args: any[] + ): EmberRunTimer; + schedule( + queue: EmberRunQueues, + method: (args: any[]) => any, + ...args: any[] + ): EmberRunTimer; + /** + * Invokes the passed target/method and optional arguments after a specified + * period of time. The last parameter of this method must always be a number + * of milliseconds. + */ + later(method: (...args: any[]) => any, wait: number): EmberRunTimer; + later(target: Target, method: RunMethod, wait: number): EmberRunTimer; + later( + target: Target, + method: RunMethod, + arg0: any, + wait: number + ): EmberRunTimer; + later( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + wait: number + ): EmberRunTimer; + later( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + wait: number + ): EmberRunTimer; + later( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + wait: number + ): EmberRunTimer; + later( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + arg4: any, + wait: number + ): EmberRunTimer; + later( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + arg4: any, + arg5: any, + wait: number + ): EmberRunTimer; + /** + * Schedule a function to run one time during the current RunLoop. This is equivalent + * to calling `scheduleOnce` with the "actions" queue. + */ + once(target: Target, method: RunMethod, ...args: any[]): EmberRunTimer; + /** + * Schedules a function to run one time in a given queue of the current RunLoop. + * Calling this method with the same queue/target/method combination will have + * no effect (past the initial call). + */ + scheduleOnce( + queue: EmberRunQueues, + target: Target, + method: RunMethod, + ...args: any[] + ): EmberRunTimer; + /** + * Schedules an item to run from within a separate run loop, after + * control has been returned to the system. This is equivalent to calling + * `run.later` with a wait time of 1ms. + */ + next(target: Target, method: RunMethod, ...args: any[]): EmberRunTimer; + /** + * Cancels a scheduled item. Must be a value returned by `run.later()`, + * `run.once()`, `run.scheduleOnce()`, `run.next()`, `run.debounce()`, or + * `run.throttle()`. + */ + cancel(timer: EmberRunTimer): boolean; + /** + * Delay calling the target method until the debounce period has elapsed + * with no additional debounce calls. If `debounce` is called again before + * the specified time has elapsed, the timer is reset and the entire period + * must pass again before the target method is called. + */ + debounce( + method: (...args: any[]) => any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + arg0: any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + arg4: any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + debounce( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + arg4: any, + arg5: any, + wait: number, + immediate?: boolean + ): EmberRunTimer; + /** + * Ensure that the target method is never called more frequently than + * the specified spacing period. The target method is called immediately. + */ + throttle( + method: (...args: any[]) => any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + arg0: any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + arg4: any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + throttle( + target: Target, + method: RunMethod, + arg0: any, + arg1: any, + arg2: any, + arg3: any, + arg4: any, + arg5: any, + spacing: number, + immediate?: boolean + ): EmberRunTimer; + + queues: EmberRunQueues[]; + }; + const platform: { + defineProperty: boolean; + hasPropertyAccessors: boolean; + }; + + /** + * `getEngineParent` retrieves an engine instance's parent instance. + */ + function getEngineParent(engine: EngineInstance): EngineInstance; + /** + * Display a deprecation warning with the provided message and a stack trace + * (Chrome and Firefox only). + */ + function deprecate( + message: string, + test: boolean, + options: { id: string; until: string } + ): any; + /** + * @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options + */ + function deprecate( + message: string, + test: boolean, + options?: { id?: string; until?: string } + ): any; + /** + * Define an assertion that will throw an exception if the condition is not met. + */ + function assert(desc: string, test?: boolean): void | never; + /** + * Display a debug notice. + */ + function debug(message: string): void; + /** + * NOTE: This is a low-level method used by other parts of the API. + * You almost never want to call this method directly. Instead you + * should use Ember.mixin() to define new properties. + */ + function defineProperty( + obj: object, + keyName: string, + desc?: PropertyDescriptor | ComputedProperty, + data?: any, + meta?: any + ): void; + /** + * Alias an old, deprecated method with its new counterpart. + */ + function deprecateFunc any)>( + message: string, + options: { id: string; until: string }, + func: Func + ): Func; + /** + * @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options + */ + function deprecateFunc any)>( + message: string, + func: Func + ): Func; + /** + * Run a function meant for debugging. + */ + function runInDebug(func: () => void): any; + /** + * Display a warning with the provided message. + */ + function warn(message: string, test: boolean, options: { id: string }): any; + function warn(message: string, options: { id: string }): any; + /** + * @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options + */ + function warn(message: string, test: boolean, options?: { id?: string }): any; + /** + * @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options + */ + function warn(message: string, options?: { id?: string }): any; + /** + * Global helper method to create a new binding. Just pass the root object + * along with a `to` and `from` path to create and connect the binding. + * @deprecated https://emberjs.com/deprecations/v2.x#toc_ember-binding + */ + function bind(obj: {}, to: string, from: string): Binding; + /** + * Returns the cached value for a property, if one exists. + * This can be useful for peeking at the value of a computed + * property that is generated lazily, without accidentally causing + * it to be created. + */ + function cacheFor( + obj: ComputedPropertyGetters, + key: K + ): T[K] | undefined; + /** + * Add an event listener + */ + function addListener( + obj: Context, + key: keyof Context, + target: Target, + method: ObserverMethod, + once?: boolean + ): void; + function addListener( + obj: Context, + key: keyof Context, + method: ObserverMethod + ): void; + /** + * Remove an event listener + */ + function removeListener( + obj: Context, + key: keyof Context, + target: Target, + method: ObserverMethod + ): any; + function removeListener( + obj: Context, + key: keyof Context, + method: ObserverMethod + ): any; + /** + * Send an event. The execution of suspended listeners + * is skipped, and once listeners are removed. A listener without + * a target is executed on the passed object. If an array of actions + * is not passed, the actions stored on the passed object are invoked. + */ + function sendEvent(obj: any, eventName: string, params?: any[], actions?: any[]): boolean; + /** + * Define a property as a function that should be executed when + * a specified event or events are triggered. + */ + function on(eventNames: string, func: (...args: any[]) => void): (...args: any[]) => void; + /** + * To get multiple properties at once, call `Ember.getProperties` + * with an object followed by a list of strings or an array: + */ + function getProperties( + obj: ComputedPropertyGetters, + list: K[] + ): Pick; + function getProperties(obj: T, list: K[]): Pick; // for dynamic K + function getProperties( + obj: ComputedPropertyGetters, + ...list: K[] + ): Pick; + function getProperties(obj: T, ...list: K[]): Pick; // for dynamic K + /** + * A value is blank if it is empty or a whitespace string. + */ + function isBlank(obj: any): boolean; + /** + * Verifies that a value is `null` or an empty string, empty array, + * or empty function. + */ + function isEmpty(obj: any): boolean; + /** + * Returns true if the passed value is null or undefined. This avoids errors + * from JSLint complaining about use of ==, which can be technically + * confusing. + */ + function isNone(obj: any): obj is null | undefined; + /** + * A value is present if it not `isBlank`. + */ + function isPresent(obj: any): boolean; + /** + * Merge the contents of two objects together into the first object. + * @deprecated Use Object.assign + */ + function merge(original: T, updates: U): T & U; + /** + * Makes a method available via an additional name. + */ + function aliasMethod(methodName: string): ComputedProperty; + /** + * Specify a method that observes property changes. + */ + function observer(key1: string, func: (target: any, key: string) => void): void; + function observer( + key1: string, + key2: string, + func: (target: any, key: string) => void + ): void; + function observer( + key1: string, + key2: string, + key3: string, + func: (target: any, key: string) => void + ): void; + function observer( + key1: string, + key2: string, + key3: string, + key4: string, + func: (target: any, key: string) => void + ): void; + function observer( + key1: string, + key2: string, + key3: string, + key4: string, + key5: string, + func: (target: any, key: string) => void + ): void; + /** + * Adds an observer on a property. + */ + function addObserver( + obj: Context, + key: keyof Context, + target: Target, + method: ObserverMethod + ): void; + function addObserver( + obj: Context, + key: keyof Context, + method: ObserverMethod + ): void; + /** + * Remove an observer you have previously registered on this object. Pass + * the same key, target, and method you passed to `addObserver()` and your + * target will no longer receive notifications. + */ + function removeObserver( + obj: Context, + key: keyof Context, + target: Target, + method: ObserverMethod + ): any; + function removeObserver( + obj: Context, + key: keyof Context, + method: ObserverMethod + ): any; + /** + * Gets the value of a property on an object. If the property is computed, + * 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(obj: ComputedPropertyGetters, key: K): T[K]; + function get(obj: T, key: K): T[K]; // for dynamic K + /** + * Retrieves the value of a property from an Object, or a default value in the + * case that the property returns `undefined`. + */ + function getWithDefault( + obj: ComputedPropertyGetters, + key: K, + defaultValue: T[K] + ): T[K]; + function getWithDefault(obj: T, key: K, defaultValue: T[K]): T[K]; // for dynamic 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( + obj: ComputedPropertySetters, + key: K, + value: V + ): V; + function set(obj: T, key: K, value: V): V; // for dynamic K + /** + * Error-tolerant form of `Ember.set`. Will not blow up if any part of the + * chain is `undefined`, `null`, or destroyed. + */ + function trySet(root: object, path: string, value: any): any; + /** + * Set a list of properties on an object. These properties are set inside + * a single `beginPropertyChanges` and `endPropertyChanges` batch, so + * observers will be buffered. + */ + function setProperties( + obj: ComputedPropertySetters, + hash: Pick + ): Pick; + function setProperties(obj: T, hash: Pick): Pick; // for dynamic K + /** + * Detects when a specific package of Ember (e.g. 'Ember.Application') + * has fully loaded and is available for extension. + */ + function onLoad(name: string, callback: Function): any; + /** + * Called when an Ember.js package (e.g Ember.Application) has finished + * loading. Triggers any callbacks registered for this event. + */ + function runLoadHooks(name: string, object?: {}): any; + /** + * Creates an `Ember.NativeArray` from an Array like object. + * Does not modify the original object's contents. Ember.A is not needed if + * `EmberENV.EXTEND_PROTOTYPES` is `true` (the default value). However, + * it is recommended that you use Ember.A when creating addons for + * ember or when you can not guarantee that `EmberENV.EXTEND_PROTOTYPES` + * will be `true`. + */ + function A(arr?: T[]): NativeArray; + /** + * Compares two javascript values and returns: + */ + function compare(v: any, w: any): number; + /** + * Creates a shallow copy of the passed object. A deep copy of the object is + * returned if the optional `deep` argument is `true`. + */ + function copy(obj: any, deep?: boolean): any; + /** + * Compares two objects, returning true if they are equal. + */ + function isEqual(a: any, b: any): boolean; + /** + * Returns true if the passed object is an array or Array-like. + */ + function isArray(obj: any): obj is ArrayLike; + /** + * Returns a consistent type for the passed object. + */ + function typeOf(item: any): string; + /** + * Copy properties from a source object to a target object. + * @deprecated Use Object.assign + */ + function assign(target: T, source: U): T & U; + function assign(target: T, source1: U, source2: V): T & U & V; + function assign(target: T, source1: U, source2: V, source3: W): T & U & V & W; + /** + * Polyfill for Object.create + * @deprecated Use Object.create + */ + function create(o: object | null): any; + /** + * Polyfill for Object.keys + * @deprecated Use Object.keys + */ + function keys(o: any): string[]; + /** + * Returns a unique id for the object. If the object does not yet have a guid, + * one will be assigned to it. You can call this on any object, + * `Ember.Object`-based or not, but be aware that it will add a `_guid` + * property. + */ + function guidFor(obj: any): string; + /** + * Convenience method to inspect an object. This method will attempt to + * convert the object into a useful string description. + */ + function inspect(obj: any): string; + /** + * Checks to see if the `methodName` exists on the `obj`, + * and if it does, invokes it with the arguments passed. + */ + function tryInvoke(obj: any, methodName: string, args?: any[]): any; + /** + * Forces the passed object to be part of an array. If the object is already + * an array, it will return the object. Otherwise, it will add the object to + * an array. If obj is `null` or `undefined`, it will return an empty array. + */ + function makeArray(obj?: T[] | T | null): T[]; + /** + * Framework objects in an Ember application (components, services, routes, etc.) + * are created via a factory and dependency injection system. Each of these + * objects is the responsibility of an "owner", which handled its + * instantiation and manages its lifetime. + */ + function getOwner(object: any): any; + /** + * `setOwner` forces a new owner on a given object instance. This is primarily + * useful in some testing cases. + */ + function setOwner(object: any, owner: any): void; + /** + * A function may be assigned to `Ember.onerror` to be called when Ember + * internals encounter an error. This is useful for specialized error handling + * and reporting code. + */ + function onerror(error: Error): void; + /** + * An empty function useful for some operations. Always returns `this`. + * @deprecated https://emberjs.com/deprecations/v2.x/#toc_code-ember-k-code + */ + function K(this: This): This; + /** + * The semantic version + */ + const VERSION: string; + /** + * Alias for jQuery + */ + const $: JQueryStatic; + /** + * This property indicates whether or not this application is currently in + * testing mode. This is set when `setupForTesting` is called on the current + * application. + */ + const testing: boolean; + + const instrument: typeof Instrumentation.instrument; + + const reset: typeof Instrumentation.reset; + + const subscribe: typeof Instrumentation.subscribe; + + const unsubscribe: typeof Instrumentation.unsubscribe; + /** + * Expands `pattern`, invoking `callback` for each expansion. + */ + function expandProperties(pattern: string, callback: (expanded: string) => void): void; + } + + type RouteModel = object | string | number; + // https://emberjs.com/api/ember/2.18/classes/RouterService + /** + * The Router service is the public API that provides component/view layer access to the router. + */ + export class RouterService extends Ember.Service { + // + /** + Name of the current route. + This property represent the logical name of the route, + which is comma separated. + For the following router: + ```app/router.js + Router.map(function() { + this.route('about'); + this.route('blog', function () { + this.route('post', { path: ':post_id' }); + }); + }); + ``` + It will return: + * `index` when you visit `/` + * `about` when you visit `/about` + * `blog.index` when you visit `/blog` + * `blog.post` when you visit `/blog/some-post-id` + */ + readonly currentRouteName: string; + // + /** + Current URL for the application. + This property represent the URL path for this route. + For the following router: + ```app/router.js + Router.map(function() { + this.route('about'); + this.route('blog', function () { + this.route('post', { path: ':post_id' }); + }); + }); + ``` + It will return: + * `/` when you visit `/` + * `/about` when you visit `/about` + * `/blog` when you visit `/blog` + * `/blog/some-post-id` when you visit `/blog/some-post-id` + */ + readonly currentURL: string; + // + /** + * Determines whether a route is active. + * + * @param routeName the name of the route + * @param models the model(s) or identifier(s) to be used while + * transitioning to the route + * @param options optional hash with a queryParams property containing a + * mapping of query parameters + */ + isActive(routeName: string, options?: { queryParams: object }): boolean; + isActive(routeName: string, models: RouteModel, options?: { queryParams: object }): boolean; + isActive(routeName: string, modelsA: RouteModel, modelsB: RouteModel, options?: { queryParams: object }): boolean; + isActive(routeName: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, options?: { queryParams: object }): boolean; + isActive(routeName: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, modelsD: RouteModel, options?: { queryParams: object }): boolean; + + // https://emberjs.com/api/ember/2.18/classes/RouterService/methods/isActive?anchor=replaceWith + /** + * Transition into another route while replacing the current URL, if + * possible. The route may be either a single route or route path. + * + * @param routeNameOrUrl the name of the route or a URL + * @param models the model(s) or identifier(s) to be used while + * transitioning to the route. + * @param options optional hash with a queryParams property + * containing a mapping of query parameters + * @returns the Transition object associated with this attempted transition + */ + replaceWith(routeNameOrUrl: string, options?: { queryParams: object }): Ember.Transition; + replaceWith(routeNameOrUrl: string, models: RouteModel, options?: { queryParams: object }): Ember.Transition; + replaceWith(routeNameOrUrl: string, modelsA: RouteModel, modelsB: RouteModel, options?: { queryParams: object }): Ember.Transition; + replaceWith(routeNameOrUrl: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, options?: { queryParams: object }): Ember.Transition; + replaceWith(routeNameOrUrl: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, modelsD: RouteModel, options?: { queryParams: object }): Ember.Transition; + + // https://emberjs.com/api/ember/2.18/classes/RouterService/methods/isActive?anchor=transitionTo + /** + * Transition the application into another route. The route may be + * either a single route or route path + * + * @param routeNameOrUrl the name of the route or a URL + * @param models the model(s) or identifier(s) to be used while + * transitioning to the route. + * @param options optional hash with a queryParams property + * containing a mapping of query parameters + * @returns the Transition object associated with this attempted transition + */ + transitionTo(routeNameOrUrl: string, options?: { queryParam: object }): Ember.Transition; + transitionTo(routeNameOrUrl: string, models: RouteModel, options?: { queryParams: object }): Ember.Transition; + transitionTo(routeNameOrUrl: string, modelsA: RouteModel, modelsB: RouteModel, options?: { queryParams: object }): Ember.Transition; + transitionTo(routeNameOrUrl: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, options?: { queryParams: object }): Ember.Transition; + transitionTo(routeNameOrUrl: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, modelsD: RouteModel, options?: { queryParams: object }): Ember.Transition; + + // https://emberjs.com/api/ember/2.18/classes/RouterService/methods/isActive?anchor=urlFor + /** + * Generate a URL based on the supplied route name. + * + * @param routeName the name of the route or a URL + * @param models the model(s) or identifier(s) to be used while + * transitioning to the route. + * @param options optional hash with a queryParams property containing + * a mapping of query parameters + * @returns the string representing the generated URL + */ + urlFor(routeName: string, options?: { queryParams: object }): string; + urlFor(routeName: string, models: RouteModel, options?: { queryParams: object }): string; + urlFor(routeName: string, modelsA: RouteModel, modelsB: RouteModel, options?: { queryParams: object }): string; + urlFor(routeName: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, options?: { queryParams: object }): string; + urlFor(routeName: string, modelsA: RouteModel, modelsB: RouteModel, modelsC: RouteModel, modelsD: RouteModel, options?: { queryParams: object }): string; + } + + module '@ember/service' { + interface Registry { + 'router': RouterService; + } + } + + export default Ember; +} + +declare module '@ember/application' { + import Ember from 'ember'; + export default class Application extends Ember.Application { } + export const getOwner: typeof Ember.getOwner; + export const onLoad: typeof Ember.onLoad; + export const runLoadHooks: typeof Ember.runLoadHooks; + export const setOwner: typeof Ember.setOwner; +} + +declare module '@ember/application/deprecations' { + import Ember from 'ember'; + export const deprecate: typeof Ember.deprecate; + export const deprecateFunc: typeof Ember.deprecateFunc; +} + +declare module '@ember/application/globals-resolver' { + import Ember from 'ember'; + export default class GlobalsResolver extends Ember.DefaultResolver { } +} + +declare module '@ember/application/instance' { + import Ember from 'ember'; + export default class ApplicationInstance extends Ember.ApplicationInstance { } +} + +declare module '@ember/application/resolver' { + import Ember from 'ember'; + export default class Resolver extends Ember.Resolver { } +} + +declare module '@ember/array' { + import Ember from 'ember'; + type EmberArray = Ember.Array; + const EmberArray: typeof Ember.Array; + export default EmberArray; + export const A: typeof Ember.A; + export const isArray: typeof Ember.isArray; + export const makeArray: typeof Ember.makeArray; +} + +declare module '@ember/array/mutable' { + import Ember from 'ember'; + type MutableArray = Ember.MutableArray; + const MutableArray: typeof Ember.MutableArray; + export default MutableArray; +} + +declare module '@ember/array/proxy' { + import Ember from 'ember'; + export default class ArrayProxy extends Ember.ArrayProxy { } +} + +declare module '@ember/component' { + import Ember from 'ember'; + export default class Component extends Ember.Component { } +} + +declare module '@ember/component/checkbox' { + import Ember from 'ember'; + export default class Checkbox extends Ember.Checkbox { } +} + +declare module '@ember/component/helper' { + import Ember from 'ember'; + export default class Helper extends Ember.Helper { } + /** + * In many cases, the ceremony of a full `Helper` class is not required. + * The `helper` method create pure-function helpers without instances. For + * example: + * ```app/helpers/format-currency.js + * import { helper } from '@ember/component/helper'; + * export default helper(function(params, hash) { + * let cents = params[0]; + * let currency = hash.currency; + * return `${currency}${cents * 0.01}`; + * }); + * ``` + */ + export function helper(helperFn: (params: any[], hash?: any) => any): any; +} + +declare module '@ember/component/text-area' { + import Ember from 'ember'; + export default class TextArea extends Ember.TextArea { } +} + +declare module '@ember/component/text-field' { + import Ember from 'ember'; + export default class TextField extends Ember.TextField { } +} + +declare module '@ember/controller' { + import Ember from 'ember'; + export default class Controller extends Ember.Controller { } + export const inject: typeof Ember.inject.controller; + + // A type registry for Ember `Controller`s. Meant to be declaration-merged + // so string lookups resolve to the correct type. + export interface Registry {} +} + +declare module '@ember/debug' { + import Ember from 'ember'; + export const assert: typeof Ember.assert; + export const debug: typeof Ember.debug; + export const inspect: typeof Ember.inspect; + export const registerDeprecationHandler: typeof Ember.Debug.registerDeprecationHandler; + export const registerWarnHandler: typeof Ember.Debug.registerWarnHandler; + export const runInDebug: typeof Ember.runInDebug; + export const warn: typeof Ember.warn; +} + +declare module '@ember/debug/container-debug-adapter' { + import Ember from 'ember'; + export default class ContainerDebugAdapter extends Ember.ContainerDebugAdapter { } +} + +declare module '@ember/debug/data-adapter' { + import Ember from 'ember'; + export default class DataAdapter extends Ember.DataAdapter { } +} + +declare module '@ember/engine' { + import Ember from 'ember'; + export default class Engine extends Ember.Engine { } + export const getEngineParent: typeof Ember.getEngineParent; +} + +declare module '@ember/engine/instance' { + import Ember from 'ember'; + export default class EngineInstance extends Ember.EngineInstance { } +} + +declare module '@ember/enumerable' { + import Ember from 'ember'; + type Enumerable = Ember.Enumerable; + const Enumerable: typeof Ember.Enumerable; + export default Enumerable; +} + +declare module '@ember/error' { + import Ember from 'ember'; + const Error: typeof Ember.Error; + export default Error; +} + +declare module '@ember/instrumentation' { + import Ember from 'ember'; + export const instrument: typeof Ember.instrument; + export const reset: typeof Ember.reset; + export const subscribe: typeof Ember.subscribe; + export const unsubscribe: typeof Ember.unsubscribe; +} + +declare module '@ember/map' { + import Ember from 'ember'; + export default class EmberMap extends Ember.Map { } +} + +declare module '@ember/map/with-default' { + import Ember from 'ember'; + export default class MapWithDefault extends Ember.MapWithDefault { } +} + +declare module '@ember/object' { + import Ember from 'ember'; + export default class EmberObject extends Ember.Object { } + export const aliasMethod: typeof Ember.aliasMethod; + export const computed: typeof Ember.computed; + export const defineProperty: typeof Ember.defineProperty; + export const get: typeof Ember.get; + export const getProperties: typeof Ember.getProperties; + export const getWithDefault: typeof Ember.getWithDefault; + export const observer: typeof Ember.observer; + export const set: typeof Ember.set; + export const setProperties: typeof Ember.setProperties; + export const trySet: typeof Ember.trySet; +} + +declare module '@ember/object/computed' { + import Ember from 'ember'; + export default class ComputedProperty extends Ember.ComputedProperty { } + export const alias: typeof Ember.computed.alias; + export const and: typeof Ember.computed.and; + export const bool: typeof Ember.computed.bool; + export const collect: typeof Ember.computed.collect; + export const deprecatingAlias: typeof Ember.computed.deprecatingAlias; + export const empty: typeof Ember.computed.empty; + export const equal: typeof Ember.computed.equal; + export const expandProperties: typeof Ember.expandProperties; + export const filter: typeof Ember.computed.filter; + export const filterBy: typeof Ember.computed.filterBy; + export const gt: typeof Ember.computed.gt; + export const gte: typeof Ember.computed.gte; + export const intersect: typeof Ember.computed.intersect; + export const lt: typeof Ember.computed.lt; + export const lte: typeof Ember.computed.lte; + export const map: typeof Ember.computed.map; + export const mapBy: typeof Ember.computed.mapBy; + export const match: typeof Ember.computed.match; + export const max: typeof Ember.computed.max; + export const min: typeof Ember.computed.min; + export const none: typeof Ember.computed.none; + export const not: typeof Ember.computed.not; + export const notEmpty: typeof Ember.computed.notEmpty; + export const oneWay: typeof Ember.computed.oneWay; + export const or: typeof Ember.computed.or; + export const readOnly: typeof Ember.computed.readOnly; + export const reads: typeof Ember.computed.reads; + export const setDiff: typeof Ember.computed.setDiff; + export const sort: typeof Ember.computed.sort; + export const sum: typeof Ember.computed.sum; + export const union: typeof Ember.computed.union; + export const uniq: typeof Ember.computed.uniq; + export const uniqBy: typeof Ember.computed.uniqBy; +} + +declare module '@ember/object/core' { + import Ember from 'ember'; + export default class CoreObject extends Ember.CoreObject { } +} + +declare module '@ember/object/evented' { + import Ember from 'ember'; + type Evented = Ember.Evented; + const Evented: typeof Ember.Evented; + export default Evented; + export const on: typeof Ember.on; +} + +declare module '@ember/object/events' { + import Ember from 'ember'; + export const addListener: typeof Ember.addListener; + export const removeListener: typeof Ember.removeListener; + export const sendEvent: typeof Ember.sendEvent; +} + +declare module '@ember/object/internals' { + import Ember from 'ember'; + export const cacheFor: typeof Ember.cacheFor; + export const copy: typeof Ember.copy; + export const guidFor: typeof Ember.guidFor; +} + +declare module '@ember/object/mixin' { + import Ember from 'ember'; + export default class Mixin extends Ember.Mixin {} +} + +declare module '@ember/object/observable' { + import Ember from 'ember'; + type Observable = Ember.Observable; + const Observable: typeof Ember.Observable; + export default Observable; +} + +declare module '@ember/object/observers' { + import Ember from 'ember'; + export const addObserver: typeof Ember.addObserver; + export const removeObserver: typeof Ember.removeObserver; +} + +declare module '@ember/object/promise-proxy-mixin' { + import Ember from 'ember'; + type PromiseProxyMixin = Ember.PromiseProxyMixin; + const PromiseProxyMixin: typeof Ember.PromiseProxyMixin; + export default PromiseProxyMixin; +} + +declare module '@ember/object/proxy' { + import Ember from 'ember'; + export default class ObjectProxy extends Ember.ObjectProxy { } +} + +declare module '@ember/polyfills' { + import Ember from 'ember'; + export const assign: typeof Ember.assign; + export const create: typeof Ember.create; + export const hasPropertyAccessors: typeof Ember.platform.hasPropertyAccessors; + export const keys: typeof Ember.keys; + export const merge: typeof Ember.merge; +} + +declare module '@ember/routing/auto-location' { + import Ember from 'ember'; + export default class AutoLocation extends Ember.AutoLocation { } +} + +declare module '@ember/routing/hash-location' { + import Ember from 'ember'; + export default class HashLocation extends Ember.HashLocation { } +} + +declare module '@ember/routing/history-location' { + import Ember from 'ember'; + export default class HistoryLocation extends Ember.HistoryLocation { } +} + +declare module '@ember/routing/link-component' { + import Ember from 'ember'; + export default class LinkComponent extends Ember.LinkComponent { } +} + +declare module '@ember/routing/location' { + import Ember from 'ember'; + const Location: typeof Ember.Location; + export default Location; +} + +declare module '@ember/routing/none-location' { + import Ember from 'ember'; + export default class NoneLocation extends Ember.NoneLocation { } +} + +declare module '@ember/routing/route' { + import Ember from 'ember'; + export default class Route extends Ember.Route { } +} + +declare module '@ember/routing/router' { + import Ember from 'ember'; + export default class EmberRouter extends Ember.Router { } +} + +declare module '@ember/routing/router-service' { + import { RouterService } from 'ember'; + export default class extends RouterService { } +} + +declare module '@ember/runloop' { + import Ember from 'ember'; + export const begin: typeof Ember.run.begin; + export const bind: typeof Ember.run.bind; + export const cancel: typeof Ember.run.cancel; + export const debounce: typeof Ember.run.debounce; + export const end: typeof Ember.run.end; + export const join: typeof Ember.run.join; + export const later: typeof Ember.run.later; + export const next: typeof Ember.run.next; + export const once: typeof Ember.run.once; + export const run: typeof Ember.run; + export const schedule: typeof Ember.run.schedule; + export const scheduleOnce: typeof Ember.run.scheduleOnce; + export const throttle: typeof Ember.run.throttle; +} + +declare module '@ember/service' { + import Ember from 'ember'; + export default class Service extends Ember.Service { } + export const inject: typeof Ember.inject.service; + + // A type registry for Ember `Service`s. Meant to be declaration-merged so + // string lookups resolve to the correct type. + interface Registry {} +} + +declare module '@ember/string' { + import Ember from 'ember'; + export const camelize: typeof Ember.String.camelize; + export const capitalize: typeof Ember.String.capitalize; + export const classify: typeof Ember.String.classify; + export const dasherize: typeof Ember.String.dasherize; + export const decamelize: typeof Ember.String.decamelize; + export const fmt: typeof Ember.String.fmt; + export const htmlSafe: typeof Ember.String.htmlSafe; + export const isHTMLSafe: typeof Ember.String.isHTMLSafe; + export const loc: typeof Ember.String.loc; + export const underscore: typeof Ember.String.underscore; + export const w: typeof Ember.String.w; +} + +declare module '@ember/test' { + import Ember from 'ember'; + export const registerAsyncHelper: typeof Ember.Test.registerAsyncHelper; + export const registerHelper: typeof Ember.Test.registerHelper; + export const registerWaiter: typeof Ember.Test.registerWaiter; + export const unregisterHelper: typeof Ember.Test.unregisterHelper; + export const unregisterWaiter: typeof Ember.Test.unregisterWaiter; +} + +declare module '@ember/test/adapter' { + import Ember from 'ember'; + export default class TestAdapter extends Ember.Test.Adapter { } +} + +declare module '@ember/utils' { + import Ember from 'ember'; + export const compare: typeof Ember.compare; + export const isBlank: typeof Ember.isBlank; + export const isEmpty: typeof Ember.isEmpty; + export const isEqual: typeof Ember.isEqual; + export const isNone: typeof Ember.isNone; + export const isPresent: typeof Ember.isPresent; + export const tryInvoke: typeof Ember.tryInvoke; + export const typeOf: typeof Ember.typeOf; +} + +declare module 'htmlbars-inline-precompile' { + interface TemplateFactory { + __htmlbars_inline_precompile_template_factory: any; + } + export default function hbs(tagged: TemplateStringsArray): TemplateFactory; +} diff --git a/types/ember/v2/test/application-instance.ts b/types/ember/v2/test/application-instance.ts new file mode 100644 index 0000000000..9ae22e5f26 --- /dev/null +++ b/types/ember/v2/test/application-instance.ts @@ -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`

Hello World

`); + +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(); +}()); diff --git a/types/ember/v2/test/application.ts b/types/ember/v2/test/application.ts new file mode 100755 index 0000000000..d0fc2e9c44 --- /dev/null +++ b/types/ember/v2/test/application.ts @@ -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 + } +}); diff --git a/types/ember/v2/test/array-ext.ts b/types/ember/v2/test/array-ext.ts new file mode 100755 index 0000000000..490fcd77e7 --- /dev/null +++ b/types/ember/v2/test/array-ext.ts @@ -0,0 +1,26 @@ +import Ember from 'ember'; +import { assertType } from './lib/assert'; + +declare global { + interface Array extends Ember.ArrayPrototypeExtensions {} +} + +class Person extends Ember.Object { + name: string; +} + +const person = Person.create({ name: 'Joe' }); +const array = [person]; + +assertType(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(array.get('firstObject')); +assertType(array.mapBy('name')); +assertType(array.map(p => p.get('name'))); +assertType(array.sortBy('name')); +assertType(array.uniq()); +assertType(array.uniqBy('name')); diff --git a/types/ember/v2/test/array-proxy.ts b/types/ember/v2/test/array-proxy.ts new file mode 100644 index 0000000000..dceea7b847 --- /dev/null +++ b/types/ember/v2/test/array-proxy.ts @@ -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 extends Ember.ArrayProxy { + isNew = true; +} + +let x = MyNewProxy.create({ content: Ember.A([1, 2, 3]) }) as MyNewProxy; +assertType(x.get('firstObject')); +assertType(x.isNew); diff --git a/types/ember/v2/test/array.ts b/types/ember/v2/test/array.ts new file mode 100755 index 0000000000..8416a4a9ec --- /dev/null +++ b/types/ember/v2/test/array.ts @@ -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(people.get('length')); +assertType(people.get('lastObject')); +assertType(people.isAny('isHappy')); +assertType(people.isAny('isHappy', 'false')); +assertType>(people.filterBy('isHappy')); +assertType>(people.rejectBy('isHappy')); +assertType>(people.filter((person) => person.get('name') === 'Yehuda')); +assertType(people.get('[]')); +assertType(people.get('[]').get('firstObject')); + +assertType>(people.mapBy('isHappy')); +assertType(people.mapBy('name.length')); + +const last = people.get('lastObject'); +if (last) { + assertType(last.get('name')); +} + +const first = people.get('lastObject'); +if (first) { + assertType(first.get('isHappy')); +} + +const letters: Ember.Enumerable = Ember.A(['a', 'b', 'c']); +const codes: number[] = letters.map((item, index, enumerable) => { + assertType(item); + assertType(index); + return item.charCodeAt(0); +}); + +let value = '1,2,3'; +let filters = Ember.A(value.split(',')); +filters.push('4'); +filters.sort(); diff --git a/types/ember/v2/test/component.ts b/types/ember/v2/test/component.ts new file mode 100755 index 0000000000..bcc314300d --- /dev/null +++ b/types/ember/v2/test/component.ts @@ -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` +
+ {{yield}} +
+ `, +}); + +Component.extend({ + layout: 'my-layout', +}); + +const MyComponent = Component.extend(); +assertType(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 + }, +}); diff --git a/types/ember/v2/test/computed.ts b/types/ember/v2/test/computed.ts new file mode 100755 index 0000000000..f93b07a0f5 --- /dev/null +++ b/types/ember/v2/test/computed.ts @@ -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(() => 'test'), + + fullName: Ember.computed('firstName', 'lastName', function() { + return `${this.get('firstName')} ${this.get('lastName')}`; + }), + + fullNameReadonly: Ember.computed('fullName', function() { + return this.get('fullName'); + }).readOnly(), + + fullNameWritable: Ember.computed('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('fullName', { + get() { + return this.get('fullName'); + } + }), + + fullNameSetOnly: Ember.computed('firstName', 'lastName', { + set(key, value) { + let [first, last] = value.split(' '); + this.set('firstName', first); + this.set('lastName', last); + return value; + } + }), + + combinators: Ember.computed(function() { + return this.get('firstName'); + }).property('firstName') + .meta({ foo: 'bar' }) + .volatile() + .readOnly(), + + explicitlyDeclared: alias('fullName') as Computed, +}); + +const person = Person.create({ + firstName: 'Fred', + lastName: 'Smith', + age: 29, +}); + +assertType(person.firstName); +assertType(person.age); +assertType>(person.noArgs); +assertType>(person.fullName); +assertType>(person.fullNameReadonly); +assertType>(person.fullNameWritable); +assertType>(person.fullNameGetOnly); +assertType>(person.fullNameSetOnly); +assertType>(person.combinators); +assertType>(person.explicitlyDeclared); + +assertType(person.get('firstName')); +assertType(person.get('age')); +assertType(person.get('noArgs')); +assertType(person.get('fullName')); +assertType(person.get('fullNameReadonly')); +assertType(person.get('fullNameWritable')); +assertType(person.get('fullNameGetOnly')); +assertType(person.get('fullNameSetOnly')); +assertType(person.get('combinators')); +assertType(person.get('explicitlyDeclared')); + +assertType<{ firstName: string, fullName: string, age: number }>(person.getProperties('firstName', 'fullName', 'age')); + +const person2 = Person.create({ + fullName: 'Fred Smith' +}); + +assertType(person2.get('firstName')); +assertType(person2.get('fullName')); + +const person3 = Person.extend({ + firstName: 'Fred', + fullName: 'Fred Smith' +}).create(); + +assertType(person3.get('firstName')); +assertType(person3.get('fullName')); + +const person4 = Person.extend({ + firstName: Ember.computed(() => 'Fred'), + fullName: Ember.computed(() => 'Fred Smith') +}).create(); + +assertType(person4.get('firstName')); +assertType(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(component2.get('isAnimal')); diff --git a/types/ember/v2/test/controller.ts b/types/ember/v2/test/controller.ts new file mode 100755 index 0000000000..02307f447c --- /dev/null +++ b/types/ember/v2/test/controller.ts @@ -0,0 +1,11 @@ +import Controller from '@ember/controller'; + +Controller.extend ({ + queryParams: ['category'], + category: null, + isExpanded: false, + + toggleBody() { + this.toggleProperty('isExpanded'); + } +}); diff --git a/types/ember/v2/test/create-negative.ts b/types/ember/v2/test/create-negative.ts new file mode 100644 index 0000000000..f0e32a2d5e --- /dev/null +++ b/types/ember/v2/test/create-negative.ts @@ -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(); diff --git a/types/ember/v2/test/create.ts b/types/ember/v2/test/create.ts new file mode 100755 index 0000000000..5bd8d2a396 --- /dev/null +++ b/types/ember/v2/test/create.ts @@ -0,0 +1,39 @@ +import Ember from 'ember'; +import { assertType } from './lib/assert'; + +const o = Ember.Object.create(); +assertType(o); + +const o1 = Ember.Object.create({x: 9}); +assertType(o1.x); + +const obj = Ember.Object.create({ a: 1 }, { b: 2 }, { c: 3 }); +assertType(obj.b); +assertType(obj.a); +assertType(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(p.firstName); +assertType>(p.fullName); +assertType(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(p4.firstName); +assertType(p4.fullName); diff --git a/types/ember/v2/test/detect-instance.ts b/types/ember/v2/test/detect-instance.ts new file mode 100755 index 0000000000..1b25beb244 --- /dev/null +++ b/types/ember/v2/test/detect-instance.ts @@ -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(testObject.foo); +} + +if (ES6Class.detectInstance(testObject)) { + assertType(testObject.bar); +} diff --git a/types/ember/v2/test/detect.ts b/types/ember/v2/test/detect.ts new file mode 100755 index 0000000000..cd54880f9f --- /dev/null +++ b/types/ember/v2/test/detect.ts @@ -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(TestClass.create().foo); +} + +if (ES6Class.detect(TestClass)) { + assertType(TestClass.create().bar); +} diff --git a/types/ember/v2/test/ember-tests.ts b/types/ember/v2/test/ember-tests.ts new file mode 100755 index 0000000000..c049512eb0 --- /dev/null +++ b/types/ember/v2/test/ember-tests.ts @@ -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('' + 'some value' + '') +); + +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((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; + +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'), +}); diff --git a/types/ember/v2/test/engine-instance.ts b/types/ember/v2/test/engine-instance.ts new file mode 100644 index 0000000000..c74914dea5 --- /dev/null +++ b/types/ember/v2/test/engine-instance.ts @@ -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(); +}()); diff --git a/types/ember/v2/test/error.ts b/types/ember/v2/test/error.ts new file mode 100644 index 0000000000..ad3c2ee8c3 --- /dev/null +++ b/types/ember/v2/test/error.ts @@ -0,0 +1,6 @@ +import { assertType } from "./lib/assert"; + +import Ember from "ember"; +import EmberError from "@ember/error"; + +assertType(EmberError); diff --git a/types/ember/v2/test/event.ts b/types/ember/v2/test/event.ts new file mode 100755 index 0000000000..c934072440 --- /dev/null +++ b/types/ember/v2/test/event.ts @@ -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() { + } + }); +} diff --git a/types/ember/v2/test/extend.ts b/types/ember/v2/test/extend.ts new file mode 100755 index 0000000000..a54f81a600 --- /dev/null +++ b/types/ember/v2/test/extend.ts @@ -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(Person.prototype.firstName); +assertType<() => string>(Person.prototype.getFullName); + +const person = Person.create({ + firstName: 'Joe', + lastName: 'Blow', + extra: 42 +}); + +assertType(person.getFullName()); +assertType(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(ES6Person.prototype.firstName); +assertType(ES6Person.prototype.fullName); + +const es6Person = ES6Person.create({ + firstName: 'Joe', + lastName: 'Blow', + extra: 42 +}); + +assertType(es6Person.fullName); +assertType(es6Person.extra); + +class PersonWithStatics extends Ember.Object { + static isPerson = true; +} +const PersonWithStatics2 = PersonWithStatics.extend({}); +class PersonWithStatics3 extends PersonWithStatics {} +class PersonWithStatics4 extends PersonWithStatics2 {} +assertType(PersonWithStatics.isPerson); +assertType(PersonWithStatics2.isPerson); +assertType(PersonWithStatics3.isPerson); +assertType(PersonWithStatics4.isPerson); diff --git a/types/ember/v2/test/function-ext.ts b/types/ember/v2/test/function-ext.ts new file mode 100755 index 0000000000..37d831d49a --- /dev/null +++ b/types/ember/v2/test/function-ext.ts @@ -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'), +}); diff --git a/types/ember/v2/test/helper.ts b/types/ember/v2/test/helper.ts new file mode 100755 index 0000000000..2aa79a5ba1 --- /dev/null +++ b/types/ember/v2/test/helper.ts @@ -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, + 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); diff --git a/types/ember/v2/test/inject.ts b/types/ember/v2/test/inject.ts new file mode 100755 index 0000000000..3ea0176ea6 --- /dev/null +++ b/types/ember/v2/test/inject.ts @@ -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(this.get('misc')); + const url = this.get('router').urlFor('some-route', 1, 2, 3, { queryParams: { seriously: 'yes' } }); + assertType(url); + if (!this.get('auth').isAuthenticated) { + this.get('applicationController').transitionToLogin(); + } + } +} diff --git a/types/ember/v2/test/lib/assert.ts b/types/ember/v2/test/lib/assert.ts new file mode 100755 index 0000000000..d6748cd5bc --- /dev/null +++ b/types/ember/v2/test/lib/assert.ts @@ -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(value: T): void; diff --git a/types/ember/v2/test/mixin.ts b/types/ember/v2/test/mixin.ts new file mode 100755 index 0000000000..a89f490a11 --- /dev/null +++ b/types/ember/v2/test/mixin.ts @@ -0,0 +1,57 @@ +import Ember from 'ember'; +import { assertType } from "./lib/assert"; + +interface EditableMixin { + edit(): void; + isEditing: boolean; +} + +const EditableMixin: Ember.Mixin = 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(comment.isEditing); +assertType(comment.postId); + +const LiteralMixins = Ember.Object.extend({ a: 1 }, { b: 2 }, { c: 3 }); +const obj = LiteralMixins.create(); +assertType(obj.a); +assertType(obj.b); +assertType(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(editableAndCancelable.isEditing); +assertType(editableAndCancelable.cancelled); diff --git a/types/ember/v2/test/object.ts b/types/ember/v2/test/object.ts new file mode 100755 index 0000000000..e1eefb75e9 --- /dev/null +++ b/types/ember/v2/test/object.ts @@ -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); + } +} diff --git a/types/ember/v2/test/observable.ts b/types/ember/v2/test/observable.ts new file mode 100755 index 0000000000..a642da271f --- /dev/null +++ b/types/ember/v2/test/observable.ts @@ -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(function() { + return this.get('name').toUpperCase(); + }) +}); + +const pojo = { name: 'Fred', age: 29 }; + +function testGet() { + assertType(Ember.get(person, 'name')); + assertType(Ember.get(person, 'age')); + assertType(Ember.get(person, 'capitalized')); + assertType(person.get('name')); + assertType(person.get('age')); + assertType(person.get('capitalized')); + assertType(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(Ember.getWithDefault(person, 'name', 'Joe')); + assertType(Ember.getWithDefault(person, 'age', 20)); + assertType(Ember.getWithDefault(person, 'capitalized', 'JOE')); + assertType(person.getWithDefault('name', 'Joe')); + assertType(person.getWithDefault('age', 20)); + assertType(person.getWithDefault('capitalized', 'JOE')); + assertType(Ember.getWithDefault(pojo, 'name', 'JOE')); +} + +function testSet() { + assertType(Ember.set(person, 'name', 'Joe')); + assertType(Ember.set(person, 'age', 35)); + assertType(Ember.set(person, 'capitalized', 'JOE')); + assertType(person.set('name', 'Joe')); + assertType(person.set('age', 35)); + assertType(person.set('capitalized', 'JOE')); + assertType(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(Ember.get(obj, 'dummy')); + assertType(Ember.get(obj, dynamicKey)); + assertType(Ember.getWithDefault(obj, 'dummy', 'default')); + assertType(Ember.getWithDefault(obj, dynamicKey, 'default')); + assertType<{ dummy: any }>(Ember.getProperties(obj, 'dummy')); + assertType<{ dummy: any }>(Ember.getProperties(obj, [ 'dummy' ])); + assertType(Ember.getProperties(obj, dynamicKey)); + assertType(Ember.getProperties(obj, [ dynamicKey ])); + assertType(Ember.set(obj, 'dummy', 'value')); + assertType(Ember.set(obj, dynamicKey, 'value')); + assertType<{ dummy: string }>(Ember.setProperties(obj, { dummy: 'value '})); + assertType(Ember.setProperties(obj, { [dynamicKey]: 'value' })); +} diff --git a/types/ember/v2/test/reopen.ts b/types/ember/v2/test/reopen.ts new file mode 100755 index 0000000000..6c13ca4155 --- /dev/null +++ b/types/ember/v2/test/reopen.ts @@ -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.reopen()); + +assertType(Person.create().name); +assertType(Person.create().sayHello()); + +const Person2 = Person.reopenClass({ + species: 'Homo sapiens', + + createPerson(name: string): Person { + return Person.create({ name }); + } +}); + +assertType(Person2.create().name); +assertType(Person2.create().sayHello()); +assertType(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; + +const ResizableTextArea = Ember.TextArea.reopen(AutoResizeMixin, { + scaling: 1.0 +}); +const text = ResizableTextArea.create(); +assertType(text.resizable); +assertType(text.scaling); + +const Reopened = Ember.Object.reopenClass({ a: 1 }, { b: 2 }, { c: 3 }); +assertType(Reopened.a); +assertType(Reopened.b); +assertType(Reopened.c); diff --git a/types/ember/v2/test/route.ts b/types/ember/v2/test/route.ts new file mode 100755 index 0000000000..84e432dbd3 --- /dev/null +++ b/types/ember/v2/test/route.ts @@ -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 {} + +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, { }); + } +} diff --git a/types/ember/v2/test/router.ts b/types/ember/v2/test/router.ts new file mode 100755 index 0000000000..760e9ed5bc --- /dev/null +++ b/types/ember/v2/test/router.ts @@ -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' }}); + } +}); diff --git a/types/ember/v2/test/run.ts b/types/ember/v2/test/run.ts new file mode 100755 index 0000000000..8d1c82e6e3 --- /dev/null +++ b/types/ember/v2/test/run.ts @@ -0,0 +1,204 @@ +import Ember from 'ember'; +import RSVP from 'rsvp'; +import { run } from '@ember/runloop'; +import { assertType } from "./lib/assert"; + +assertType(Ember.run.queues); + +function testRun() { + let r = run(function() { + // code to be executed within a RunLoop + return 123; + }); + assertType(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); +} diff --git a/types/ember/v2/test/test.ts b/types/ember/v2/test/test.ts new file mode 100755 index 0000000000..6cd1b2b908 --- /dev/null +++ b/types/ember/v2/test/test.ts @@ -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(); + }); + }); +}); diff --git a/types/ember/v2/test/transition.ts b/types/ember/v2/test/transition.ts new file mode 100755 index 0000000000..3a64675c24 --- /dev/null +++ b/types/ember/v2/test/transition.ts @@ -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: 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'); + } + } + } +}); diff --git a/types/ember/v2/test/utils.ts b/types/ember/v2/test/utils.ts new file mode 100755 index 0000000000..d729d472b8 --- /dev/null +++ b/types/ember/v2/test/utils.ts @@ -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(Ember.makeArray()); + assertType(Ember.makeArray(null)); + assertType(Ember.makeArray(undefined)); + assertType(Ember.makeArray('lindsay')); + assertType(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(newMethod('first', 123)); + assertType(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}`; + })); +} diff --git a/types/ember/v2/test/view-utils.ts b/types/ember/v2/test/view-utils.ts new file mode 100644 index 0000000000..a3613064eb --- /dev/null +++ b/types/ember/v2/test/view-utils.ts @@ -0,0 +1,5 @@ +import Ember from 'ember'; +import { assertType } from './lib/assert'; + +const { ViewUtils: { isSimpleClick } } = Ember; +assertType(isSimpleClick(new Event('wat'))); diff --git a/types/ember/v2/tsconfig.json b/types/ember/v2/tsconfig.json new file mode 100755 index 0000000000..3269fa01f1 --- /dev/null +++ b/types/ember/v2/tsconfig.json @@ -0,0 +1,59 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": false, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "paths": { + "ember": ["ember/v2"] + }, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "test/lib/assert.ts", + "test/application.ts", + "test/application-instance.ts", + "test/engine-instance.ts", + "test/ember-tests.ts", + "test/error.ts", + "test/event.ts", + "test/extend.ts", + "test/create.ts", + "test/create-negative.ts", + "test/object.ts", + "test/observable.ts", + "test/mixin.ts", + "test/reopen.ts", + "test/detect.ts", + "test/detect-instance.ts", + "test/array.ts", + "test/array-ext.ts", + "test/array-proxy.ts", + "test/helper.ts", + "test/computed.ts", + "test/component.ts", + "test/function-ext.ts", + "test/inject.ts", + "test/utils.ts", + "test/transition.ts", + "test/router.ts", + "test/run.ts", + "test/test.ts", + "test/controller.ts", + "test/route.ts", + "test/view-utils.ts" + ] +} diff --git a/types/ember/v2/tslint.json b/types/ember/v2/tslint.json new file mode 100755 index 0000000000..f77f8d2ed8 --- /dev/null +++ b/types/ember/v2/tslint.json @@ -0,0 +1,30 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + // Heavy use of Function type in this older package. + "ban-types": false, + "jsdoc-format": false, + "no-misused-new": false, + + // these are disabled because of rfc176 module exports + "strict-export-declare-modifiers": false, + "no-single-declare-module": false, + "no-declare-current-package": false, + "no-self-import": false, + + // We use interfaces in a number of places to express things (including + // mixins in particular, but also including extending a global + // interface) which TS currently can't express correctly. + "no-empty-interface": false, + + "no-duplicate-imports": false, + "no-unnecessary-qualifier": false, + "prefer-const": false, + "no-void-expression": false, + "only-arrow-functions": false, + "no-submodule-imports": false, + + // false positives + "unified-signatures": false + } +} diff --git a/types/ember__test-helpers/index.d.ts b/types/ember__test-helpers/index.d.ts index 067222b425..bd7720e3b1 100644 --- a/types/ember__test-helpers/index.d.ts +++ b/types/ember__test-helpers/index.d.ts @@ -2,8 +2,9 @@ // Project: https://github.com/emberjs/ember-test-helpers // Definitions by: Dan Freeman // James C. Davis +// Mike North // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.6 +// TypeScript Version: 2.8 ///