Skip to content

Commit

Permalink
feat: improve typing around Model and createRecord (#9450)
Browse files Browse the repository at this point in the history
* types: cleanup Model types

* make model types more powerful

* fixup

* fixups

* fixup

* more type fixus

* more work

* upates

* fix lint
  • Loading branch information
runspired committed May 20, 2024
1 parent 3aedda7 commit ffc8b50
Show file tree
Hide file tree
Showing 20 changed files with 467 additions and 138 deletions.
3 changes: 2 additions & 1 deletion packages/-ember-data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"scripts": {
"lint": "eslint . --quiet --cache --cache-strategy=content --report-unused-disable-directives",
"build:pkg": "vite build;",
"prepack": "bun run _build && cd ../../ && bun run build:docs"
"prepack": "bun run build:pkg && cd ../../ && bun run build:docs",
"sync-hardlinks": "bun run sync-dependencies-meta-injected"
},
"ember-addon": {
"main": "addon-main.cjs",
Expand Down
14 changes: 7 additions & 7 deletions packages/-ember-data/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,25 @@ export default class Store extends BaseStore {
this.registerSchema(buildSchema(this));
}

override createCache(storeWrapper: CacheCapabilitiesManager): Cache {
createCache(storeWrapper: CacheCapabilitiesManager): Cache {
return new JSONAPICache(storeWrapper);
}

override instantiateRecord(
instantiateRecord(
this: ModelStore,
identifier: StableRecordIdentifier,
createRecordArgs: Record<string, unknown>
): Model {
return instantiateRecord.call(this, identifier, createRecordArgs);
}

override teardownRecord(record: Model): void {
teardownRecord(record: Model): void {
teardownRecord.call(this, record);
}

override modelFor<T>(type: TypeFromInstance<T>): ModelSchema<T>;
override modelFor(type: string): ModelSchema;
override modelFor(type: string): ModelSchema {
modelFor<T>(type: TypeFromInstance<T>): ModelSchema<T>;
modelFor(type: string): ModelSchema;
modelFor(type: string): ModelSchema {
return (modelFor.call(this, type) as ModelSchema) || super.modelFor(type);
}

Expand All @@ -66,7 +66,7 @@ export default class Store extends BaseStore {
normalize = normalize;
serializeRecord = serializeRecord;

override destroy() {
destroy() {
cleanup.call(this);
super.destroy();
}
Expand Down
14 changes: 7 additions & 7 deletions packages/adapter/src/json-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ import RESTAdapter from './rest';
@extends RESTAdapter
*/
class JSONAPIAdapter extends RESTAdapter {
override _defaultContentType = 'application/vnd.api+json';
_defaultContentType = 'application/vnd.api+json';

/**
@method ajaxOptions
Expand All @@ -172,7 +172,7 @@ class JSONAPIAdapter extends RESTAdapter {
@param {Object} options
@return {Object}
*/
override ajaxOptions(
ajaxOptions(
url: string,
type: HTTPMethod,
options: JQueryAjaxSettings | RequestInit = {}
Expand Down Expand Up @@ -240,29 +240,29 @@ class JSONAPIAdapter extends RESTAdapter {
@public
@type {boolean}
*/
override get coalesceFindRequests() {
get coalesceFindRequests() {
const coalesceFindRequests = this._coalesceFindRequests;
if (typeof coalesceFindRequests === 'boolean') {
return coalesceFindRequests;
}
return (this._coalesceFindRequests = false);
}

override set coalesceFindRequests(value: boolean) {
set coalesceFindRequests(value: boolean) {
this._coalesceFindRequests = value;
}

override findMany(store: Store, type: ModelSchema, ids: string[], snapshots: Snapshot[]): Promise<AdapterPayload> {
findMany(store: Store, type: ModelSchema, ids: string[], snapshots: Snapshot[]): Promise<AdapterPayload> {
const url = this.buildURL(type.modelName, ids, snapshots, 'findMany');
return this.ajax(url, 'GET', { data: { filter: { id: ids.join(',') } } });
}

override pathForType(modelName: string): string {
pathForType(modelName: string): string {
const dasherized = dasherize(modelName);
return pluralize(dasherized);
}

override updateRecord(store: Store, schema: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
updateRecord(store: Store, schema: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
const data = serializeIntoHash(store, schema, snapshot);
const type = snapshot.modelName;
const id = snapshot.id;
Expand Down
20 changes: 10 additions & 10 deletions packages/adapter/src/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,15 +433,15 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@public
@type {boolean}
*/
override get coalesceFindRequests() {
get coalesceFindRequests() {
const coalesceFindRequests = this._coalesceFindRequests;
if (typeof coalesceFindRequests === 'boolean') {
return coalesceFindRequests;
}
return (this._coalesceFindRequests = false);
}

override set coalesceFindRequests(value: boolean) {
set coalesceFindRequests(value: boolean) {
this._coalesceFindRequests = value;
}

Expand Down Expand Up @@ -526,7 +526,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {Snapshot} snapshot
@return {Promise} promise
*/
override findRecord(store: Store, type: ModelSchema, id: string, snapshot: Snapshot): Promise<AdapterPayload> {
findRecord(store: Store, type: ModelSchema, id: string, snapshot: Snapshot): Promise<AdapterPayload> {
const url = this.buildURL(type.modelName, id, snapshot, 'findRecord');
const query: QueryState = this.buildQuery(snapshot);

Expand All @@ -548,7 +548,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {SnapshotRecordArray} snapshotRecordArray
@return {Promise} promise
*/
override findAll(
findAll(
store: Store,
type: ModelSchema,
sinceToken: null,
Expand Down Expand Up @@ -584,7 +584,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {Object} adapterOptions
@return {Promise} promise
*/
override query(store: Store, type: ModelSchema, query: Record<string, unknown>): Promise<AdapterPayload> {
query(store: Store, type: ModelSchema, query: Record<string, unknown>): Promise<AdapterPayload> {
const url = this.buildURL(type.modelName, null, null, 'query', query);

if (this.sortQueryParams) {
Expand Down Expand Up @@ -614,7 +614,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {Object} adapterOptions
@return {Promise} promise
*/
override queryRecord(
queryRecord(
store: Store,
type: ModelSchema,
query: Record<string, unknown>,
Expand Down Expand Up @@ -789,7 +789,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {Snapshot} snapshot
@return {Promise} promise
*/
override createRecord(store: Store, type: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
createRecord(store: Store, type: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
const url = this.buildURL(type.modelName, null, snapshot, 'createRecord');

const data = serializeIntoHash(store, type, snapshot);
Expand All @@ -814,7 +814,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {Snapshot} snapshot
@return {Promise} promise
*/
override updateRecord(store: Store, schema: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
updateRecord(store: Store, schema: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
const data = serializeIntoHash(store, schema, snapshot, {});
const type = snapshot.modelName;
const id = snapshot.id;
Expand All @@ -836,7 +836,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@param {Snapshot} snapshot
@return {Promise} promise
*/
override deleteRecord(store: Store, schema: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
deleteRecord(store: Store, schema: ModelSchema, snapshot: Snapshot): Promise<AdapterPayload> {
const type = snapshot.modelName;
const id = snapshot.id;
assert(`Attempted to delete the ${type} record, but the record has no id`, typeof id === 'string' && id.length > 0);
Expand Down Expand Up @@ -897,7 +897,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) {
@return {Array} an array of arrays of records, each of which is to be
loaded separately by `findMany`.
*/
override groupRecordsForFindMany(store: Store, snapshots: Snapshot[]): Snapshot[][] {
groupRecordsForFindMany(store: Store, snapshots: Snapshot[]): Snapshot[][] {
const groups: Map<string, Snapshot[]> = new Map();
const maxURLLength = this.maxURLLength;

Expand Down
22 changes: 11 additions & 11 deletions packages/debug/src/data-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,15 @@ export default class extends DataAdapter<Model> {
@return {Array} List of objects defining filters
The object should have a `name` and `desc` property
*/
override getFilters() {
getFilters() {
return [
{ name: 'isNew', desc: 'New' },
{ name: 'isModified', desc: 'Modified' },
{ name: 'isClean', desc: 'Clean' },
];
}

override _nameToClass(type: string) {
_nameToClass(type: string) {
return this.store.modelFor(type);
}

Expand All @@ -185,7 +185,7 @@ export default class extends DataAdapter<Model> {
Takes an array of objects containing wrapped types.
@return {Function} Method to call to remove all observers
*/
override watchModelTypes(typesAdded: WrappedTypeCallback, typesUpdated: WrappedTypeCallback) {
watchModelTypes(typesAdded: WrappedTypeCallback, typesUpdated: WrappedTypeCallback) {
const { store } = this;

const discoveredTypes = typesMapFor(store);
Expand Down Expand Up @@ -277,7 +277,7 @@ export default class extends DataAdapter<Model> {
name: {String} The name of the column
desc: {String} Humanized description (what would show in a table column name)
*/
override columnsForType(typeClass: ModelSchema) {
columnsForType(typeClass: ModelSchema) {
const columns = [
{
name: 'id',
Expand Down Expand Up @@ -306,7 +306,7 @@ export default class extends DataAdapter<Model> {
This array will be observed for changes,
so it should update when new records are added/removed
*/
override getRecords(modelClass: ModelSchema, modelName: string) {
getRecords(modelClass: ModelSchema, modelName: string) {
if (arguments.length < 2) {
// Legacy Ember.js < 1.13 support
const containerKey = (modelClass as unknown as { _debugContainerKey?: string })._debugContainerKey;
Expand All @@ -330,15 +330,15 @@ export default class extends DataAdapter<Model> {
@param {Model} record to get values from
@return {Object} Keys should match column names defined by the model type
*/
override getRecordColumnValues(record: Model) {
getRecordColumnValues<T extends Model>(record: T) {
let count = 0;
const columnValues: Record<string, unknown> = { id: record.id };

record.eachAttribute((key) => {
if (count++ > this.attributeLimit) {
return false;
}
columnValues[key] = record[key];
columnValues[key] = record[key as keyof T];
});
return columnValues;
}
Expand All @@ -351,13 +351,13 @@ export default class extends DataAdapter<Model> {
@param {Model} record
@return {Array} Relevant keywords for search based on the record's attribute values
*/
override getRecordKeywords(record: Model): NativeArray<unknown> {
getRecordKeywords<T extends Model>(record: T): NativeArray<unknown> {
const keywords: unknown[] = [record.id];
const keys = ['id'];

record.eachAttribute((key) => {
keys.push(key);
keywords.push(record[key]);
keywords.push(record[key as keyof T]);
});

return A(keywords);
Expand All @@ -372,7 +372,7 @@ export default class extends DataAdapter<Model> {
@param {Model} record
@return {Object} The record state filter values
*/
override getRecordFilterValues(record: Model) {
getRecordFilterValues(record: Model) {
return {
isNew: record.isNew,
isModified: record.hasDirtyAttributes && !record.isNew,
Expand All @@ -389,7 +389,7 @@ export default class extends DataAdapter<Model> {
@param {Model} record
@return {String} The record color
*/
override getRecordColor(record: Model) {
getRecordColor(record: Model) {
let color = 'black';
if (record.isNew) {
color = 'green';
Expand Down
4 changes: 2 additions & 2 deletions packages/model/src/-private/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export class Errors extends ArrayProxyWithCustomOverrides<ValidationError> {
@private
*/
@computed()
override get content(): NativeArray<ValidationError> {
get content(): NativeArray<ValidationError> {
return A();
}

Expand Down Expand Up @@ -374,7 +374,7 @@ export class Errors extends ArrayProxyWithCustomOverrides<ValidationError> {
@method clear
@public
*/
override clear(): void {
clear(): void {
if (this.isEmpty) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/model/src/-private/many-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ export class RelatedCollection<T = unknown> extends LiveArray<T> {
return record;
}

override destroy() {
destroy() {
super.destroy(false);
}
}
Expand Down
9 changes: 5 additions & 4 deletions packages/model/src/-private/model-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { lookupLegacySupport } from './legacy-relationships-support';
import type RecordState from './record-state';
import type BelongsToReference from './references/belongs-to';
import type HasManyReference from './references/has-many';
import type { MaybeBelongsToFields, MaybeHasManyFields } from './type-utils';

export interface MinimalLegacyRecord {
errors: Errors;
Expand All @@ -27,8 +28,8 @@ export interface MinimalLegacyRecord {

deleteRecord(): void;
unloadRecord(): void;
save<T extends MinimalLegacyRecord>(this: T, options?: Record<string, unknown>): Promise<T>;
destroyRecord<T extends MinimalLegacyRecord>(this: T, options?: Record<string, unknown>): Promise<T>;
save<T extends MinimalLegacyRecord>(this: T, options?: Record<string, unknown>): Promise<this>;
destroyRecord<T extends MinimalLegacyRecord>(this: T, options?: Record<string, unknown>): Promise<this>;
}

export function rollbackAttributes<T extends MinimalLegacyRecord>(this: T) {
Expand All @@ -52,14 +53,14 @@ export function unloadRecord<T extends MinimalLegacyRecord>(this: T) {
this[RecordStore].unloadRecord(this);
}

export function belongsTo<T extends MinimalLegacyRecord, K extends keyof T & string>(
export function belongsTo<T extends MinimalLegacyRecord, K extends MaybeBelongsToFields<T>>(
this: T,
prop: K
): BelongsToReference<T, K> {
return lookupLegacySupport(this).referenceFor('belongsTo', prop) as BelongsToReference<T, K>;
}

export function hasMany<T extends MinimalLegacyRecord, K extends keyof T & string>(
export function hasMany<T extends MinimalLegacyRecord, K extends MaybeHasManyFields<T>>(
this: T,
prop: K
): HasManyReference<T, K> {
Expand Down
Loading

0 comments on commit ffc8b50

Please sign in to comment.