diff --git a/types/ember-data/index.d.ts b/types/ember-data/index.d.ts index f5ff0c5d57..a98082a60e 100644 --- a/types/ember-data/index.d.ts +++ b/types/ember-data/index.d.ts @@ -16,8 +16,14 @@ import ModelRegistry from 'ember-data/types/registries/model'; import SerializerRegistry from 'ember-data/types/registries/serializer'; import AdapterRegistry from 'ember-data/types/registries/adapter'; -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) +/** + The keys from the actual Model class, removing all the keys which come from + the base class. + */ +type ModelKeys = Exclude; + +type AttributesFor = ModelKeys; // TODO: filter to attr properties only (TS 2.8) +type RelationshipsFor = ModelKeys; // TODO: filter to hasMany/belongsTo properties only (TS 2.8) export interface ChangedAttributes { [key: string]: [any, any] | undefined; @@ -49,9 +55,9 @@ export namespace DS { */ function errorsArrayToHash(errors: any[]): {}; - interface RelationshipOptions { + interface RelationshipOptions { async?: boolean; - inverse?: RelationshipsFor | null; + inverse?: RelationshipsFor | null; polymorphic?: boolean; } @@ -457,12 +463,12 @@ export namespace DS { * Create a JSON representation of the record, using the serialization * strategy of the store's adapter. */ - serialize(options?: { includeId?: boolean }): {}; + serialize(options?: { includeId?: boolean }): object; /** * Use [DS.JSONSerializer](DS.JSONSerializer.html) to * get the JSON representation of a record. */ - toJSON(options: {}): {}; + toJSON(options?: { includeId?: boolean }): object; /** * Fired when the record is ready to be interacted with, * that is either loaded from the server or created locally. @@ -502,15 +508,15 @@ export namespace DS { * method if you want to allow the user to still `rollbackAttributes()` * after a delete was made. */ - deleteRecord(): any; + deleteRecord(): void; /** * Same as `deleteRecord`, but saves the record immediately. */ - destroyRecord(options?: {}): RSVP.Promise; + destroyRecord(options?: { adapterOptions?: object }): RSVP.Promise; /** * Unloads the record from the store. This will cause the record to be destroyed and freed up for garbage collection. */ - unloadRecord(): any; + unloadRecord(): void; /** * Returns an object, whose keys are changed properties, and value is * an [oldProp, newProp] array. @@ -520,16 +526,16 @@ export namespace DS { * If the model `hasDirtyAttributes` this function will discard any unsaved * changes. If the model `isNew` it will be removed from the store. */ - rollbackAttributes(): any; + rollbackAttributes(): void; /** * Save the record and persist any changes to the record to an * external source via the adapter. */ - save(options?: {}): RSVP.Promise; + save(options?: { adapterOptions?: object }): RSVP.Promise; /** * Reload the record from the adapter. */ - reload(): RSVP.Promise; + reload(options?: { adapterOptions?: object }): RSVP.Promise; /** * Get the reference for the specified belongsTo relationship. */ @@ -547,7 +553,7 @@ export namespace DS { this: T, callback: (name: string, details: RelationshipMeta) => void, binding?: any - ): any; + ): void; /** * 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. @@ -605,14 +611,14 @@ export namespace DS { static eachRelationship( callback: (name: string, details: RelationshipMeta) => void, binding?: any - ): any; + ): void; /** * 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; + static eachRelatedType(callback: (name: string) => void, binding?: any): void; /** * A map whose keys are the attributes of the model (properties * described by DS.attr) and whose values are the meta object for the @@ -630,26 +636,27 @@ export namespace DS { * Iterates through the attributes of the model, calling the passed function on each * attribute. */ - static eachAttribute(callback: Function, binding: {}): any; + static eachAttribute>( + this: Class, + callback: ( + name: ModelKeys, + meta: AttributeMeta + ) => void, + binding?: any + ): void; /** * 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; + static eachTransformedAttribute( + this: Class, + callback: ( + name: ModelKeys>, + type: keyof TransformRegistry + ) => void, + binding?: any + ): void; } /** * ### State @@ -700,7 +707,7 @@ export namespace DS { * Used to get the latest version of all of the records in this array * from the adapter. */ - update(): any; + update(): PromiseArray; /** * Saves all of the records in the `RecordArray`. */ @@ -782,7 +789,7 @@ export namespace DS { /** * `ids()` returns an array of the record ids in this relationship. */ - ids(): any[]; + ids(): string[]; /** * The meta data for the has-many relationship. */ @@ -948,7 +955,7 @@ export namespace DS { /** * Get snapshots of the underlying record array */ - snapshots(): any[]; + snapshots(): Snapshot[]; } class Snapshot { /** @@ -994,14 +1001,19 @@ export namespace DS { belongsTo>( keyName: L, options?: {} - ): Snapshot['record'][L] | string | null | undefined; + ): Snapshot | null | undefined; + belongsTo>( + keyName: L, + options: { id: true } + ): string | null | undefined; + /** * Returns the current value of a hasMany relationship. */ hasMany>( keyName: L, options?: { ids: false } - ): Array['record'][L]> | undefined; + ): Snapshot[] | undefined; hasMany>( keyName: L, options: { ids: true } @@ -1011,21 +1023,21 @@ export namespace DS { * function on each attribute. */ eachAttribute( - callback: (key: keyof M, meta: AttributeMeta) => void, + callback: (key: ModelKeys, meta: AttributeMeta) => void, binding?: {} - ): any; + ): void; /** * Iterates through all the relationships of the model, calling the passed * function on each relationship. */ eachRelationship( - callback: (key: keyof M, meta: RelationshipMeta) => void, + callback: (key: ModelKeys, meta: RelationshipMeta) => void, binding?: {} - ): any; + ): void; /** * Serializes the snapshot using the serializer for the model. */ - serialize(options: {}): {}; + serialize(options: O): object; } /** @@ -1095,7 +1107,8 @@ export namespace DS { */ query( modelName: K, - query: any + query: object, + options?: { adapterOptions?: object } ): AdapterPopulatedRecordArray & PromiseArray; /** @@ -1105,7 +1118,8 @@ export namespace DS { */ queryRecord( modelName: K, - query: any + query: object, + options?: { adapterOptions?: object } ): RSVP.Promise; /** * `findAll` asks the adapter's `findAll` method to find the records for the diff --git a/types/ember-data/test/model.ts b/types/ember-data/test/model.ts index 2085daf90a..356c607748 100644 --- a/types/ember-data/test/model.ts +++ b/types/ember-data/test/model.ts @@ -1,6 +1,7 @@ import Ember from 'ember'; import DS, { ChangedAttributes } from 'ember-data'; import { assertType } from "./lib/assert"; +import RSVP from 'rsvp'; const Person = DS.Model.extend({ firstName: DS.attr(), @@ -33,5 +34,26 @@ user.serialize(); user.serialize({ includeId: true }); user.serialize({ includeId: true }); -const attributes = user.changedAttributes(); -assertType(attributes); +const attributes: ChangedAttributes = user.changedAttributes(); + +user.rollbackAttributes(); // $ExpectType void + +let destroyResult: RSVP.Promise; +destroyResult = user.destroyRecord(); +destroyResult = user.destroyRecord({}); +destroyResult = user.destroyRecord({ adapterOptions: {}}); +destroyResult = user.destroyRecord({ adapterOptions: { waffles: 'are yummy' }}); + +user.deleteRecord(); // $ExpectType void + +user.unloadRecord(); // $ExpectType void + +let jsonified: object; +jsonified = user.toJSON(); +jsonified = user.toJSON({ includeId: true }); + +let reloaded: RSVP.Promise; +reloaded = user.reload(); +reloaded = user.reload({}); +reloaded = user.reload({ adapterOptions: {} }); +reloaded = user.reload({ adapterOptions: { fastAsCanBe: 'yessirree' } }); diff --git a/types/ember-data/test/relationships.ts b/types/ember-data/test/relationships.ts index 13a526ba6a..04aae75f0e 100644 --- a/types/ember-data/test/relationships.ts +++ b/types/ember-data/test/relationships.ts @@ -1,5 +1,7 @@ import Ember from 'ember'; import DS from 'ember-data'; +import TransformRegistry from 'ember-data/types/registries/transform'; +import { assertType } from './lib/assert'; declare const store: DS.Store; @@ -8,24 +10,65 @@ const Person = DS.Model.extend({ parent: DS.belongsTo('folder', { inverse: 'children' }) }); +// $ExpectType void +Person.eachAttribute(() => {}); +// $ExpectType void +Person.eachAttribute(() => {}, {}); +// $ExpectType void +Person.eachAttribute((name, meta) => { + assertType<'children' | 'parent'>(name); + assertType<{ + type: keyof TransformRegistry; + options: object; + name: 'children' | 'parent'; + parentType: DS.Model; + isAttribute: true; + }>(meta); +}); + +// $ExpectType void +Person.eachTransformedAttribute(() => {}); +// $ExpectType void +Person.eachTransformedAttribute(() => {}, {}); +// $ExpectType void +Person.eachTransformedAttribute((name, type) => { + assertType<'children' | 'parent'>(name); + let t: keyof TransformRegistry = type; +}); + const Polymorphic = DS.Model.extend({ paymentMethods: DS.hasMany('payment-method', { polymorphic: true }) }); +// $ExpectType void Polymorphic.eachRelationship(() => ''); +// $ExpectType void Polymorphic.eachRelationship(() => '', {}); +// $ExpectType void Polymorphic.eachRelationship((n, meta) => { let s: string = n; let m: 'belongsTo' | 'hasMany' = meta.kind; }); let p = Polymorphic.create(); +// $ExpectType void p.eachRelationship(() => ''); +// $ExpectType void p.eachRelationship(() => '', {}); +// $ExpectType void p.eachRelationship((n, meta) => { let s: string = n; let m: 'belongsTo' | 'hasMany' = meta.kind; }); +// $ExpectType void +Polymorphic.eachRelatedType(() => ''); +// $ExpectType void +Polymorphic.eachRelatedType(() => '', {}); +// $ExpectType void +Polymorphic.eachRelatedType((name) => { + let s: string = name; +}); + export class Comment extends DS.Model { author = DS.attr('string'); } diff --git a/types/ember-data/test/serializer.ts b/types/ember-data/test/serializer.ts index d38ced647a..490dc77493 100644 --- a/types/ember-data/test/serializer.ts +++ b/types/ember-data/test/serializer.ts @@ -1,6 +1,10 @@ import Ember from 'ember'; import DS from 'ember-data'; +interface Dict { + [key: string]: T | null | undefined; +} + const JsonApi = DS.JSONAPISerializer.extend({}); const Customized = DS.JSONAPISerializer.extend({ @@ -74,14 +78,16 @@ const SerializerUsingSnapshots = DS.RESTSerializer.extend({ DS.Serializer.extend({ serialize(snapshot: DS.Snapshot<'message-for-serializer'>, options: {}) { - let json: any = { + let json: Dict = { id: snapshot.id }; + // $ExpectType void snapshot.eachAttribute((key, attribute) => { json[key] = snapshot.attr(key); }); + // $ExpectType void snapshot.eachRelationship((key, relationship) => { if (relationship.kind === 'belongsTo') { json[key] = snapshot.belongsTo(key, { id: true }); diff --git a/types/ember-data/test/store.ts b/types/ember-data/test/store.ts index b6bb25993c..f692a668d9 100644 --- a/types/ember-data/test/store.ts +++ b/types/ember-data/test/store.ts @@ -24,6 +24,7 @@ let post = store.createRecord('post', { }); post.save(); // => POST to '/posts' +post.save({ adapterOptions: { makeItSo: 'number one ' } }); post.save().then(saved => { assertType(saved); });