From 3718dbe64f446f074a03ff63536efb99bf84d90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zab=C5=82ocki?= Date: Mon, 29 Jul 2019 23:05:26 +0200 Subject: [PATCH] overhaul typings based on Model declaration merging --- types/redux-orm/Model.d.ts | 607 ++++++++++++----------------- types/redux-orm/ORM.d.ts | 68 ++-- types/redux-orm/QuerySet.d.ts | 133 +++---- types/redux-orm/Session.d.ts | 24 +- types/redux-orm/constants.d.ts | 25 +- types/redux-orm/db/Database.d.ts | 96 ++--- types/redux-orm/db/Table.d.ts | 91 ++--- types/redux-orm/db/index.d.ts | 4 +- types/redux-orm/fields.d.ts | 36 +- types/redux-orm/helpers.d.ts | 17 +- types/redux-orm/index.d.ts | 64 +-- types/redux-orm/redux-orm-tests.ts | 62 ++- types/redux-orm/redux.d.ts | 78 ++-- 13 files changed, 510 insertions(+), 795 deletions(-) diff --git a/types/redux-orm/Model.d.ts b/types/redux-orm/Model.d.ts index 7ce2794..bfe11ee 100644 --- a/types/redux-orm/Model.d.ts +++ b/types/redux-orm/Model.d.ts @@ -1,49 +1,8 @@ -import { Attribute, AttributeWithDefault, FieldSpecMap, ForeignKey, OneToOne } from './fields'; -import { KnownKeys, OptionalKeys, PickByValue } from './helpers'; +import { Attribute, AttributeWithDefault, ForeignKey, ManyToMany, OneToOne } from './fields'; +import { OptionalKeys, PickByValue, Serializable } from './helpers'; import QuerySet, { MutableQuerySet } from './QuerySet'; -import { OrmSession } from './Session'; import { Table } from './db'; - -/** - * A primitive value - */ -export type Primitive = number | string | boolean; - -/** - * Serializable value: a primitive, undefined, a serializable object or an array of those - */ -export type Serializable = - | Primitive - | Primitive[] - | undefined - | { - [K: string]: Serializable | Serializable[]; - }; - -/** - * A union of supported model field types - * - * Specify foreign key and one-to-one association properties as Model typed properties. - * - * Specify many-to-many and reverse-fk associations as related Model's specification of: - * - {@link MutableQuerySet} - for many-to-many relations - * - {@link QuerySet} - for reverse side of foreign keys - */ -export type ModelField = QuerySet | SessionBoundModel | Serializable; - -/** - * Map of fields restriction to supported field types. - */ -export interface ModelFieldMap { - [K: string]: ModelField; -} - -/** - * A Model-derived mapped type for supplying relations and alike. - * - * Either a primitive type matching Model's identifier type or an object implementing a `{ getId(): IdType }` interface - */ -export type IdOrModelLike = IdType | { getId(): IdType }; +import { OrmSession } from './Session'; /** * The heart of an ORM, the data model. @@ -62,12 +21,15 @@ export type IdOrModelLike = IdType | { getId(): IdType }; * To create data models in your schema, subclass {@link Model}. To define * information about the data model, override static class methods. Define instance * logic by defining prototype methods (without `static` keyword). - * @borrows {@link QuerySet.filter} as Model#filter + * + * Specify foreign key and one-to-one association properties as Model typed properties. + * + * Specify many-to-many and reverse-fk associations as related Model's specification of: + * - {@link MutableQuerySet} - for many-to-many relations + * - {@link QuerySet} - for reverse side of foreign keys */ -export class Model { - /** - * A string constant identifying specific Model, necessary to retain the shape of state and relations through transpilation steps - */ +export class Model { + /** A string constant identifying specific Model. **Setting {@link Model#modelName} is mandatory.** */ static modelName: string; /** @@ -77,7 +39,7 @@ export class Model; /** * Returns the options object passed to the database for the table that represents @@ -91,12 +53,8 @@ export class Model Table.TableOpts) | Table.TableOpts; + static options: { (): Model.ModelOpts } | Model.ModelOpts; - /** - * The key of Model's identifier property - */ - static readonly idAttribute: string; /** * {@link QuerySet} class associated with this Model class. * @@ -104,105 +62,42 @@ export class Model; - __fields: Fields; - __descriptors: MClass['fields']; - __class: MClass; + /** The key of Model's identifier property. */ + static readonly idAttribute: string; - /** - * Creates a Model instance from it's properties. - * Don't use this to create a new record; Use the static method {@link Model#create}. - * @param props - the properties to instantiate with - */ - constructor(props: Fields); + /** @see {@link Model.getQuerySet} */ + static readonly query: QuerySet; /** - * Gets the {@link Model} class or subclass constructor (the class that - * instantiated this instance). + * Returns an instance of the model's {@link Model#querySetClass} field. + * By default, this will be an empty {@link QuerySet}. * - * @return The {@link Model} class or subclass constructor used to instantiate - * this instance. - */ - getClass(): MClass; - - /** - * Gets the id value of the current instance by looking up the id attribute. - * @return The id value of the current instance. - */ - getId] = Fields[Table.IdAttribute]>(): Id extends undefined ? number : Id; - - /** - * @return A string representation of this {@link Model} instance. + * @return An instance of the model's {@link Model#querySetClass}. */ - toString(): string; + static getQuerySet(this: Model.Class): QuerySet; /** - * Returns a boolean indicating if `otherModel` equals this {@link Model} instance. - * Equality is determined by shallow comparing their attributes. + * Returns a {@link Model} instance for the object with id `id`. + * Returns `null` if the model has no instance with id `id`. * - * This equality is used when you call {@link Model#update}. - * You can prevent model updates by returning `true` here. - * However, a model will always be updated if its relationships are changed. + * You can use {@link Model#idExists} to check for existence instead. * - * @param otherModel - a {@link Model} instance to compare - * @return a boolean indicating if the {@link Model} instance's are equal. + * @param id - the `id` of the object to get + * @return a {@link Model} instance with id `id` */ - equals(otherModel: Model | SessionBoundModel): boolean; + static withId(this: Model.Class, id: Model.IdType): M | null; /** - * Updates a property name to given value for this {@link Model} instance. - * The values are immediately committed to the database. + * Returns a boolean indicating if an entity + * with the id `id` exists in the state. * - * @param propertyName - name of the property to set - * @param value - value assigned to the property - */ - set(propertyName: K, value: RefPropOrSimple): void; - - /** - * Assigns multiple fields and corresponding values to this {@link Model} instance. - * The updates are immediately committed to the database. + * @param id - a value corresponding to the id attribute of the {@link Model} class. + * @return a boolean indicating if entity with `id` exists in the state * - * @param userMergeObj - an object that will be merged with this instance. - */ - update(userMergeObj: UpdateProps>): void; - - /** - * Updates {@link Model} instance attributes to reflect the - * database state in the current session. - */ - refreshFromState(): void; - - /** - * Deletes the record for this {@link Model} instance. - * Fields and values on the instance are still accessible after the call. - */ - delete(): void; - - /** - * @see {@link QuerySet.delete} + * @since 0.11.0 */ - static delete(): void; -} - -export namespace Model { - type MCtor = new (...args: any[]) => M; + static idExists(this: Model.Class, id: Model.IdType): boolean; - function reducer( - this: MCtor, - action: any, - model: M extends infer R ? (R extends Model ? ReturnType : never) : never, - session: OrmSession - ): void; /** * Creates a new record in the database, instantiates a {@link Model} and returns it. * @@ -210,12 +105,12 @@ export namespace Model { * model as well. * * @param props - the new {@link Model}'s properties. - * @return a new {@link SessionBoundModel} instance. + * @return a new {@link Model} instance. */ - function create>( - this: MCtor, + static create>( + this: Model.Class, props: TProps - ): SessionBoundModel>; + ): M & Model.CustomProps; /** * Creates a new or update existing record in the database, instantiates a {@link Model} and returns it. @@ -224,50 +119,66 @@ export namespace Model { * model as well. * * @param props - the upserted {@link Model}'s properties. - * @return a {@link SessionBoundModel} instance. + * @return a {@link Model} instance. */ - function upsert>( - this: MCtor, + static upsert>( + this: Model.Class, props: TProps - ): SessionBoundModel>; + ): M & Model.CustomProps; - /** - * Gets the {@link Model} instance that matches properties in `lookupObj`. - * Throws an error if {@link Model} if multiple records match - * the properties. - * - * @param lookupSpec - the properties used to match a single entity. - * @throws {Error} If more than one entity matches the properties in `lookupObj`. - * @return a {@link SessionBoundModel} instance that matches the properties in `lookupObj`. - */ - function get(this: MCtor, lookupSpec: QuerySet.LookupSpec): SessionBoundModel | null; + /** @see {@link QuerySet.QueryBuilder.delete} */ + static delete(): void; - /** - * Returns a {@link Model} instance for the object with id `id`. - * Returns `null` if the model has no instance with id `id`. - * - * You can use {@link Model#idExists} to check for existence instead. - * - * @param id - the `id` of the object to get - * @return a {@link SessionBoundModel} instance with id `id` - */ - function withId(this: MCtor, id: IdType): SessionBoundModel | null; + /** @see {@link QuerySet.QueryBuilder.all} */ + static all(this: Model.Class): QuerySet; + + /** @see {@link QuerySet.QueryBuilder.at} */ + static at(this: Model.Class, index: number): M | undefined; + + /** @see {@link QuerySet.QueryBuilder.first} */ + static first(this: Model.Class): M | undefined; + + /** @see {@link QuerySet.QueryBuilder.last} */ + static last(this: Model.Class): M | undefined; + + /** @see {@link QuerySet.QueryBuilder.update} */ + static update(this: Model.Class, props: Model.UpdateProps): void; + + /** @see {@link QuerySet.QueryBuilder.filter} */ + static filter(this: Model.Class, props: QuerySet.LookupSpec): QuerySet; + + /** @see {@link QuerySet.QueryBuilder.exclude} */ + static exclude(this: Model.Class, props: QuerySet.LookupSpec): QuerySet; + + /** @see {@link QuerySet.QueryBuilder.orderBy} */ + static orderBy( + this: Model.Class, + iteratees: ReadonlyArray>, + orders?: ReadonlyArray + ): QuerySet; + + /** @see {@link QuerySet.QueryBuilder.count} */ + static count(): number; /** * Returns a boolean indicating if an entity - * with the id `id` exists in the state. - * - * @param id - a value corresponding to the id attribute of the {@link Model} class. - * @return a boolean indicating if entity with `id` exists in the state + * with the given props exists in the state. * - * @since 0.11.0 + * @param props - a key-value that {@link Model} instances should have to be considered as existing. + * @return a boolean indicating if entity with `props` exists in the state */ - function idExists(this: MCtor, id: IdType): boolean; + static exists(this: Model.Class, props: QuerySet.LookupProps): boolean; /** - * @return A string representation of this {@link Model} class. + * Gets the {@link Model} instance that matches properties in `lookupObj`. + * Throws an error if {@link Model} if multiple records match + * the properties. + * + * @param lookupSpec - the properties used to match a single entity. + * @throws {Error} If more than one entity matches the properties in `lookupObj`. + * @return a {@link Model} instance that matches the properties in `lookupObj`. */ - function toString(): string; + static get(this: Model.Class, lookupSpec: QuerySet.LookupSpec): M | null; /** * Manually mark individual instances as accessed. @@ -275,241 +186,203 @@ export namespace Model { * * @param ids - Array of primary key values */ - function markAccessed(ids: Array): void; + static markAccessed(this: Model.Class, ids: Array>): void; /** * Manually mark this model's table as scanned. * This allows invalidating selector memoization within mutable sessions. - * */ - function markFullTableScanned(): void; + static markFullTableScanned(): void; - /** - * Returns an instance of the model's `querySetClass` field. - * By default, this will be an empty {@link QuerySet}. - * - * @return An instance of the model's `querySetClass`. - */ - function getQuerySet(this: MCtor): QuerySet; + static reducer( + this: Model.Class, + action: any, + model: M extends infer R ? (R extends Model ? Model.ExtractClass : never) : never, + session: OrmSession + ): void; /** - * @see {@link QuerySet.all} + * Returns a reference to the plain JS object in the store. + * Make sure to not mutate this. + * + * @return a reference to the plain JS object in the store */ - function all(this: MCtor): QuerySet; + readonly ref: Model.Ref>; /** - * @see {@link QuerySet.at} + * Creates a Model instance from it's properties. + * Don't use this to create a new record; Use the static method {@link Model#create}. + * @param props - the properties to instantiate with. */ - function at(this: MCtor, index: number): SessionBoundModel | undefined; + constructor(props: object); /** - * @see {@link QuerySet.first} + * Gets the {@link Model} class or subclass constructor (the class that + * instantiated this instance). + * + * @return The {@link Model} class or subclass constructor used to instantiate + * this instance. */ - function first(this: MCtor): SessionBoundModel | undefined; + getClass(): MClass; /** - * @see {@link QuerySet.last} + * Gets the id value of the current instance by looking up the id attribute. + * @return The id value of the current instance. */ - function last(this: MCtor): SessionBoundModel | undefined; + getId(): Model.IdType>; /** - * @see {@link QuerySet.update} + * Returns a boolean indicating if `otherModel` equals this {@link Model} instance. + * Equality is determined by shallow comparing their attributes. + * + * This equality is used when you call {@link Model#update}. + * You can prevent model updates by returning `true` here. + * However, a model will always be updated if its relationships are changed. + * + * @param otherModel - a {@link Model} instance to compare + * @return a boolean indicating if the {@link Model} instance's are equal. */ - function update(this: MCtor, props: UpdateProps): void; + equals(otherModel: Model): boolean; /** - * @see {@link QuerySet.filter} + * Updates a property name to given value for this {@link Model} instance. + * The values are immediately committed to the database. + * + * @param propertyName - name of the property to set + * @param value - value assigned to the property */ - function filter(this: MCtor, props: QuerySet.LookupSpec): QuerySet; + set(propertyName: K, value: K extends keyof this['ref'] ? this['ref'][K] : Serializable): void; /** - * @see {@link QuerySet.exclude} + * Assigns multiple fields and corresponding values to this {@link Model} instance. + * The updates are immediately committed to the database. + * + * @param userMergeObj - an object that will be merged with this instance. */ - function exclude(this: MCtor, props: QuerySet.LookupSpec): QuerySet; + update(userMergeObj: Model.UpdateProps>): void; /** - * @see {@link QuerySet.orderBy} + * Updates {@link Model} instance attributes to reflect the + * database state in the current session. */ - function orderBy( - this: MCtor, - iteratees: ReadonlyArray>, - orders?: ReadonlyArray - ): QuerySet; + refreshFromState(): void; /** - * @see {@link QuerySet.count} + * Deletes the record for this {@link Model} instance. + * Fields and values on the instance are still accessible after the call. */ - function count(): number; + delete(): void; +} +export namespace Model { /** - * Returns a boolean indicating if an entity - * with the given props exists in the state. - * - * @param props - a key-value that {@link Model} instances should have to be considered as existing. - * @return a boolean indicating if entity with `props` exists in the state + * Shape of {@link Model} constructor function, with Model configuration properties */ - function exists(this: MCtor, props: QuerySet.LookupProps): boolean; -} + interface Class { + new (...args: any[]): M; + modelName: string; + fields: Record; + options?: { (): ModelOpts } | ModelOpts; + } -/** - * Model wildcard type. - */ -export class AnyModel extends Model {} - -/** - * {@link Model#update} argument type - * - * All properties are optional. - * Supplied properties are type-checked against the type of related Model's fields. - * Relations can be provided in a flexible manner for both many-to-many and foreign key associations - * @see {@link IdOrModelLike} - */ - -/** - * @internal - */ -export type CustomInstanceProps = Omit< - Props, - Extract>> ->; - -/** - * Model id property key extraction helper. - * - * Falls back to `'id'` if not specified explicitly via {@link Model.options}. - */ -export type IdKey = Table.IdAttribute extends infer R - ? R extends keyof ModelFields - ? R - : never - : never; - -/** - * Model id property type extraction helper. - * - * Falls back to `number` if not specified explicitly via {@link Model.options}. - */ -export type IdType = FieldAt> extends string ? string : number; - -/** - * A mapped type restricting allowed types of second {@link Model.set} argument. - * Depending on the first argument `propertyName` argument, value type can be restricted to: - * - declared Model field type - if propertyName belongs to declared Model fields - * - any serializable value - if propertyName is not among declared Model fields - */ -export type RefPropOrSimple = K extends keyof M['ref'] - ? M['ref'][K] - : Serializable; - -/** - * A Model-derived mapped type, representing model instance bound to a session. - * - * SessionBoundModels relation properties for convenient association traversal. - * Custom type-checked properties are available on `SessionBoundModel` instances created using - * @link Model#create} or {@link Model#upsert} calls. - */ -export type SessionBoundModel = M & - { [K in keyof ModelFields]: FieldAt extends AnyModel ? SessionBoundModel> : FieldAt } & - InstanceProps; - -/** - * @internal - */ -export type ModelClass = ReturnType; - -/** - * @internal - */ -export type ModelFields = M['__fields']; - -export type FieldAt> = ModelFields[K]; + /** Model id property key extraction helper. */ + type IdKey = Table.IdAttribute> extends infer Id + ? Id extends keyof M + ? Id + : never + : never; -/** - * @internal - */ -export type FieldSpecKeys = Extract< - keyof ModelFields, - keyof PickByValue ->; + /** Model id property type extraction helper. */ + type IdType = M[IdKey]; -/** - * Type of {@link Model.ref} / database entry for a particular Model type - */ -// export type Ref = { -// [K in FieldSpecKeys]: FieldAt extends AnyModel -// ? IdType> -// : FieldAt; -// }; -export type Ref = { - [K in keyof PickByValue]: K extends keyof M['__fields'] - ? M['__fields'][K] extends AnyModel - ? IdType - : M['__fields'][K] - : never; -}; - -export type ModelBlueprint = ModelFields> = { - [K in keyof Fields]: Fields[K] extends AnyModel - ? IdOrModelLike - : Fields[K] extends MutableQuerySet - ? ReadonlyArray> - : Fields[K]; -}; - -export type NonBlueprintKeys = Exclude< - keyof PickByValue>, AnyModel | QuerySet>, - FieldSpecKeys | keyof PickByValue>, MutableQuerySet> ->; - -export type BlueprintProps, ReqKeys extends keyof any, OptKeys extends keyof any> = { - [K in ReqKeys]-?: M[K]; -} & - { - [K in OptKeys]+?: M[K]; + /** Type of {@link Model.ref} / database entry for a particular Model type */ + type Ref = { + [K in FieldDescriptorKeys]: M[K] extends Model ? IdType : M[K]; }; -export type IdKeyOpt = IdType extends number ? IdKey : never; - -/** - * {@link Model#create} argument type - * - * Relations can be provided in a flexible manner for both many-to-many and foreign key associations - * @see {@link IdOrModelLike} - */ -export type CreateProps< - M extends AnyModel, - Fields extends Omit, NonBlueprintKeys> = Omit, NonBlueprintKeys>, - MQsKeys extends keyof PickByValue = keyof PickByValue, - OptAttrKeys extends keyof any = FieldSpecKeys | IdKeyOpt, - OptKeys extends keyof any = MQsKeys | OptionalKeys | OptAttrKeys -> = BlueprintProps, Exclude, OptKeys>; + /** @internal */ + type ExtractClass = M extends { getClass(): infer R } ? R : never; + + /** @internal */ + type FieldDescriptorKeys = keyof PickByValue< + ExtractClass['fields'], + TField + > extends infer R + ? R extends keyof M + ? R + : never + : never; -/** - * {@link Model#upsert} argument type - * - * All properties aside from identifier are optional. - * Supplied properties are type-checked against the type of related Model's fields. - * Relations can be provided in a flexible manner for both many-to-many and foreign key associations - * @see {@link IdOrModelLike} - */ -export type UpsertProps = BlueprintProps< - ModelBlueprint, - IdKey, - Exclude, IdKey> ->; + /** @internal */ + type CustomProps = Omit>; + + /** @internal */ + type ModelBlueprint< + M extends Model, + K extends keyof M = keyof ExtractClass['fields'] | keyof PickByValue + > = { + [P in K]: M[P] extends MutableQuerySet + ? ReadonlyArray | RM> + : M[P] extends Model + ? IdType | M[P] + : M[P]; + }; -/** - * {@link Model#update} argument type - * - * All properties are optional. - * Supplied properties are type-checked against the type of related Model's fields. - * Relations can be provided in a flexible manner for both many-to-many and foreign key associations - * @see {@link IdOrModelLike} - */ -export type UpdateProps = BlueprintProps< - ModelBlueprint, - never, - Exclude, IdKey> ->; + /** @internal */ + type BlueprintProps = { + [K in ReqKeys]-?: M[K]; + } & + { + [K in OptKeys]+?: M[K]; + }; + + /** {@link Model#create} argument type. */ + type CreateProps< + M extends Model, + MB extends ModelBlueprint = ModelBlueprint, + MQsKeys extends keyof MB = keyof PickByValue, + OptAttrKeys extends keyof MB = FieldDescriptorKeys | (IdType extends number ? IdKey : never), + OptKeys extends keyof MB = MQsKeys | OptionalKeys | OptAttrKeys + > = BlueprintProps, OptKeys>; + + /** {@link Model#upsert} argument type */ + type UpsertProps = BlueprintProps< + ModelBlueprint, + IdKey, + Exclude, IdKey> + >; + + /** {@link Model#update} argument type - all properties aside from the primary key are optional. */ + type UpdateProps = BlueprintProps< + ModelBlueprint, + never, + Exclude, IdKey> + >; + + /** + * {@link ModelOpts} used for {@link Table} customization. + * + * Supplied via {@link Model#options}. + * + * If no customizations were provided, the table uses following default options: + *
+ * ```typescript + * { + * idAttribute: 'id', + * arrName: 'items', + * mapName: 'itemsById' + * } + * ``` + *
+ * @see {@link Model} + * @see {@link Model#options} + * @see {@link ORM.State} + */ + interface ModelOpts { + readonly idAttribute?: string; + readonly arrName?: string; + readonly mapName?: string; + } +} export default Model; diff --git a/types/redux-orm/ORM.d.ts b/types/redux-orm/ORM.d.ts index 635228f..732cb9b 100644 --- a/types/redux-orm/ORM.d.ts +++ b/types/redux-orm/ORM.d.ts @@ -1,5 +1,5 @@ import createDatabase, { Table, Database } from './db'; -import { AnyModel } from './Model'; +import Model from './Model'; import Session, { OrmSession } from './Session'; /** @@ -15,7 +15,7 @@ import Session, { OrmSession } from './Session'; * Internally, this class handles generating a schema specification from models * to the database. */ -export class ORM, ModelNames extends keyof I = keyof I> { +export class ORM = ORM.Registry> { /** * Creates a new ORM instance. */ @@ -30,7 +30,7 @@ export class ORM, ModelNames extends keyo * * @param model - a {@link Model} class to register */ - register(...model: ReadonlyArray): void; + register(...model: ReadonlyArray): void; /** * Gets a {@link Model} class by its name from the registry. @@ -41,40 +41,40 @@ export class ORM, ModelNames extends keyo * * @return the {@link Model} class, if found */ - get(modelName: K): I[K]; + get(modelName: K): ORegistry[K]; /** * Returns the empty database state. * - * @see {@link OrmState} + * @see {@link ORM.State} * * @return empty state */ - getEmptyState(): ORM.OrmState; + getEmptyState(): ORM.State; /** * Begins an immutable database session. * - * @see {@link OrmState} - * @see {@link SessionType} + * @see {@link ORM.State} + * @see {@link OrmSession} * * @param state - the state the database manages * - * @return a new {@link Session} instance + * @return a new {@link OrmSession} instance */ - session(state?: ORM.OrmState): OrmSession; + session(state?: ORM.State): OrmSession; /** * Begins an mutable database session. * - * @see {@link OrmState} - * @see {@link SessionType} + * @see {@link ORM.State} + * @see {@link OrmSession} * * @param state - the state the database manages * - * @return a new {@link Session} instance + * @return a new {@link OrmSession} instance */ - mutableSession(state: ORM.OrmState): OrmSession; + mutableSession(state: ORM.State): OrmSession; /** * Acquire database reference. @@ -83,37 +83,27 @@ export class ORM, ModelNames extends keyo * * @return A {@link Database} instance structured according to registered schema. */ - getDatabase(): Database; + getDatabase(): Database; } export namespace ORM { - /** - * A `{typeof Model[modelName]: typeof Model}` map defining: - * - * - database schema - * - {@link Session} bound Model classes - * - ORM branch state type - */ - type IndexedModelClasses< - T extends { [k in keyof T]: typeof AnyModel } = {}, - K extends keyof T = Extract - > = { [k in K]: T[K] }; + /** ORM instantiation opts - enables customization of database creation. */ + interface ORMOpts { + createDatabase: typeof createDatabase; + } - /** - * A mapped type capable of inferring ORM branch state type based on schema {@link Model}s. - */ - type OrmState> = { - [K in keyof MClassMap]: Table.TableState + /** Schema definition - an array of {@link Model.Class} constructor functions */ + type Schema = ReadonlyArray; + + /** A map of schema {@link Model} classes ({@link Model.Class}, indexed by `{@link Model#modelName}` */ + type Registry = { + [K in S[number]['modelName']]: Extract; }; - /** - * ORM instantiation opts. - * - * Enables customization of database creation. - */ - interface ORMOpts { - createDatabase: typeof createDatabase; - } + /** A mapped type capable of inferring ORM branch state type based on schema {@link Model.Class} . */ + type State = { + [K in keyof Registry]: Table.TableState[K]>; + }; } export default ORM; diff --git a/types/redux-orm/QuerySet.d.ts b/types/redux-orm/QuerySet.d.ts index 425257a..681f30c 100644 --- a/types/redux-orm/QuerySet.d.ts +++ b/types/redux-orm/QuerySet.d.ts @@ -1,8 +1,8 @@ import { Database } from './db'; -import Model, { AnyModel, IdType, ModelClass, Serializable, SessionBoundModel, UpdateProps } from './Model'; +import Model from './Model'; +import { Serializable } from './helpers'; /** - *

* `QuerySet` class is used to build and make queries to the database * and operating the resulting set (such as updating attributes * or deleting the records). @@ -15,9 +15,9 @@ import Model, { AnyModel, IdType, ModelClass, Serializable, SessionBoundModel, U * * @description The query is executed only when terminating operations are invoked, such as: * - * - {@link QuerySet#count}, - * - {@link QuerySet#toRefArray} - * - {@link QuerySet#at} and other indexed reads + * - {@link QuerySet.count}, + * - {@link QuerySet.toRefArray} + * - {@link QuerySet.at} and other indexed reads * * After the query is executed, the resulting set is cached in the QuerySet instance. * @@ -29,8 +29,34 @@ import Model, { AnyModel, IdType, ModelClass, Serializable, SessionBoundModel, U * * @see {@link QuerySet.QueryBuilder} */ -export class QuerySet +export class QuerySet = {}> implements QuerySet.QueryBuilder { + /** + * Register custom method on a `QuerySet` class specification. + * QuerySet class may be attached to a {@link Model} class via {@link Model#querySetClass} + * + * @param methodName - name of a method to be available on specific QuerySet class instances + * + * @example: + * class CustomQuerySet extends QuerySet { + * static currentYear = 2019 + * unreleased(): QuerySet { + * return this.filter(book => book.releaseYear > CustomQuerySet.currentYear); + * } + * } + * CustomQuerySet.addSharedMethod('unreleased'); + * // assign specific QuerySet to a Model class + * Book.querySetClass = typeof CustomQuerySet; + * // register models + * const schema = {Book }; + * const orm = new ORM(); + * orm.register(Book); + * const session = orm.session(orm.getEmptyState()); + * // use shared method + * const unreleased = customQs.unreleased(); + */ + static addSharedMethod(methodName: string): void; + /** * Creates a `QuerySet`. The constructor is mainly for internal use; * Access QuerySet instances from {@link Model}. @@ -39,7 +65,7 @@ export class QuerySet, clauses: Database.QueryClause[], opts?: object); + constructor(modelClass: typeof Model, clauses: Database.QueryClause[], opts?: object); /** * Checks if the {@link QuerySet} instance has any records matching the query @@ -58,24 +84,17 @@ export class QuerySet; /** - * Returns an array of {@link SessionBoundModel} instances represented by the QuerySet. + * Returns an array of {@link Model} instances represented by the QuerySet. * * @return session bound model instances represented by the QuerySet */ - toModelArray(): ReadonlyArray>; - - /** - * Returns a string representation of QuerySet instance contents. - * - * @return string representation of QuerySet. - */ - toString(): string; + toModelArray(): ReadonlyArray; - at(index: number): SessionBoundModel | undefined; + at(index: number): M & InstanceProps | undefined; - first(): SessionBoundModel | undefined; + first(): M & InstanceProps | undefined; - last(): SessionBoundModel | undefined; + last(): M & InstanceProps | undefined; all(): QuerySet; @@ -90,79 +109,51 @@ export class QuerySet): void; + update(mergeObj: Model.UpdateProps): void; delete(): void; } -/** - * A {@link QuerySet} extended with {@link ManyToMany} specific functionality. - */ -export interface MutableQuerySet +/** A {@link QuerySet} extended with {@link ManyToMany} specific functionality. */ +export interface MutableQuerySet = {}> extends QuerySet { - add: (...entitiesToAdd: Array | M>) => void; - remove: (...entitiesToRemove: Array | M>) => void; + add: (...entitiesToAdd: ReadonlyArray | M>) => void; + remove: (...entitiesToRemove: ReadonlyArray | M>) => void; clear: () => void; } export namespace QuerySet { - /** - * Register custom method on a `QuerySet` class specification. - * QuerySet class may be attached to a {@link Model} class via {@link Model#querySetClass} - * - * @param methodName - name of a method to be available on specific QuerySet class instances - * - * @example: - * class CustomQuerySet extends QuerySet { - * static currentYear = 2019 - * unreleased(): QuerySet { - * return this.filter(book => book.releaseYear > CustomQuerySet.currentYear); - * } - * } - * CustomQuerySet.addSharedMethod('unreleased'); - * // assign specific QuerySet to a Model class - * Book.querySetClass = typeof CustomQuerySet; - * // register models - * const schema = {Book }; - * const orm = new ORM(); - * orm.register(Book); - * const session = orm.session(orm.getEmptyState()); - * // use shared method - * const unreleased = customQs.unreleased(); - */ - function addSharedMethod(methodName: string): void; - /** * Interface for building queries in fluent style */ - interface QueryBuilder { + interface QueryBuilder = {}> { /** - * Returns the {@link SessionBoundModel} instance at index `index` in the {@link QuerySet} instance if + * Returns the {@link Model} instance at index `index` in the {@link QuerySet} instance if * `withRefs` flag is set to `false`, or a reference to the plain JavaScript * object in the model state if `true`. * * @param index - index of the model instance to get - * @return a {@link Model} derived {@link SessionBoundModel} instance at index + * @return a {@link Model} derived {@link Model} instance at index * `index` in the {@link QuerySet} instance, * or undefined if the index is out of bounds. */ - at(index: number): SessionBoundModel | undefined; + at(index: number): M & InstanceProps | undefined; /** * Returns the session bound {@link Model} instance at index 0 * in the {@link QuerySet} instance or undefined if the instance is empty. * - * @return a {@link Model} derived {@link SessionBoundModel} instance or undefined. + * @return a {@link Model} derived {@link Model} instance or undefined. */ - first(): SessionBoundModel | undefined; + first(): M & InstanceProps | undefined; /** * Returns the session bound {@link Model} instance at index `QuerySet.count() - 1` * in the {@link QuerySet} instance or undefined if the instance is empty. * - * @return a {@link Model} derived {@link SessionBoundModel} instance or undefined. + * @return a {@link Model} derived {@link Model} instance or undefined. */ - last(): SessionBoundModel | undefined; + last(): M & InstanceProps | undefined; /** * Returns a new {@link QuerySet} instance with the same entities. @@ -218,7 +209,7 @@ export namespace QuerySet { * * @param mergeObj - an object extending {@link UpdateProps}, to be merged with all the objects in this QuerySet. */ - update(mergeObj: UpdateProps): void; + update(mergeObj: Model.UpdateProps): void; /** * Records a deletion of all the objects in this {@link QuerySet} instance. @@ -234,20 +225,20 @@ export namespace QuerySet { } /** - * Optional ordering direction. + * Ordering clause. + * + * Either a key of SessionBoundModel or a evaluator function accepting plain object Model representation stored in the database. * * {@see QuerySet.orderBy} */ - type SortOrder = 'asc' | 'desc' | true | false; + type SortIteratee = keyof M['ref'] | { (row: M['ref']): any }; /** - * Ordering clause. - * - * Either a key of SessionBoundModel or a evaluator function accepting plain object Model representation stored in the database. + * Ordering direction. * * {@see QuerySet.orderBy} */ - type SortIteratee = keyof M['ref'] | { (row: M['ref']): any }; + type SortOrder = 'asc' | 'desc' | true | false; /** * Lookup clause as an object specifying props to match with plain object Model representation stored in the database. @@ -269,14 +260,6 @@ export namespace QuerySet { * {@see QuerySet.filter} */ type LookupSpec = LookupProps | LookupPredicate; - - /** - * A lookup query result. - * - * May contain additional properties in case {@link LookupProps} clause had been supplied. - * {@see QuerySet.exclude} - * {@see QuerySet.filter} - */ } export default QuerySet; diff --git a/types/redux-orm/Session.d.ts b/types/redux-orm/Session.d.ts index a86ccaf..8c8e347 100644 --- a/types/redux-orm/Session.d.ts +++ b/types/redux-orm/Session.d.ts @@ -1,39 +1,35 @@ import { ORM } from './ORM'; + +import Model from './Model'; import { Database } from './db'; export type BatchToken = any; -export default class Session> { +export default class Session { /** * list of bound {@link Model} classes bound to this session, bootstrapped during {@link @ORM.register}. * * @see {@link Model} */ - readonly sessionBoundModels: ReadonlyArray; + readonly sessionBoundModels: S; /** - * Current {@link OrmState}, specific to registered schema + * Current {@link ORM.State}, specific to registered schema */ - readonly state: ORM.OrmState; + readonly state: ORM.State; /** * Creates a new Session. * * @param schema - {@Link ORM} instance, with bootstrapped {@link Model} prototypes. * @param db - a {@link Database} instance - * @param state - the database {@link OrmState} + * @param state - the database {@link ORM.State} * @param withMutations? - whether the session should mutate data * @param batchToken? - a {@link BatchToken} used by the backend to identify objects that can be * mutated. If none is provided a default of `Symbol('ownerId')` will be created. * */ - constructor( - schema: ORM, - db: Database, - state: ORM.OrmState, - withMutations?: boolean, - batchToken?: BatchToken - ); + constructor(schema: ORM, db: Database, state: ORM.State, withMutations?: boolean, batchToken?: BatchToken); /** * Executes query against model state. @@ -54,7 +50,7 @@ export default class Session> { * * @param update - the update command object. * - * @returns query result. + * @returns applyUpdate result. * * @see {@link DbAction} * @see {@link UpdateSpec} @@ -70,4 +66,4 @@ export default class Session> { * Extension is a map of {@link Model} class accessible under keys within a set of {@link Model#modelName} values * for registered {@link Model} classes. */ -export type OrmSession> = Session & I; +export type OrmSession = Session & ORM.Registry; diff --git a/types/redux-orm/constants.d.ts b/types/redux-orm/constants.d.ts index 95654bd..64a1722 100644 --- a/types/redux-orm/constants.d.ts +++ b/types/redux-orm/constants.d.ts @@ -1,10 +1,17 @@ -export const UPDATE = 'REDUX_ORM_UPDATE'; -export const DELETE = 'REDUX_ORM_DELETE'; -export const CREATE = 'REDUX_ORM_CREATE'; +export type UPDATE = 'REDUX_ORM_UPDATE'; +export type DELETE = 'REDUX_ORM_DELETE'; +export type CREATE = 'REDUX_ORM_CREATE'; +export type FILTER = 'REDUX_ORM_FILTER'; +export type EXCLUDE = 'REDUX_ORM_EXCLUDE'; +export type ORDER_BY = 'REDUX_ORM_ORDER_BY'; +export type SUCCESS = 'SUCCESS'; +export type FAILURE = 'FAILURE'; -export const FILTER = 'REDUX_ORM_FILTER'; -export const EXCLUDE = 'REDUX_ORM_EXCLUDE'; -export const ORDER_BY = 'REDUX_ORM_ORDER_BY'; - -export const SUCCESS = 'SUCCESS'; -export const FAILURE = 'FAILURE'; +export const UPDATE: UPDATE; +export const DELETE: DELETE; +export const CREATE: CREATE; +export const FILTER: FILTER; +export const EXCLUDE: EXCLUDE; +export const ORDER_BY: ORDER_BY; +export const SUCCESS: SUCCESS; +export const FAILURE: FAILURE; diff --git a/types/redux-orm/db/Database.d.ts b/types/redux-orm/db/Database.d.ts index fd7ed0a..3134110 100644 --- a/types/redux-orm/db/Database.d.ts +++ b/types/redux-orm/db/Database.d.ts @@ -1,25 +1,25 @@ import { ORM } from '../ORM'; import { CREATE, DELETE, EXCLUDE, FAILURE, FILTER, ORDER_BY, SUCCESS, UPDATE } from '../constants'; import { Table } from './Table'; -import { Serializable } from '../Model'; +import Model from '../Model'; import { BatchToken } from '../Session'; +import { Serializable } from '../helpers'; /** * A Database parametrized by schema made of {@link Model} classes. * - * @see {@link SchemaSpec} - * @see {@link TableSpec} + * @see {@link Database.SchemaSpec} * @see {@link Table} */ -export interface Database, Tables = { [K in keyof I]: Table }> { +export interface Database { /** * Returns the empty database state. * - * @see {@link OrmState} + * @see {@link ORM.State} * * @return empty state */ - getEmptyState(): ORM.OrmState; + getEmptyState(): ORM.State; /** * Execute a query against a given state. @@ -27,13 +27,13 @@ export interface Database, Tables = { [K * @param querySpec - a query definition. * @param state - the state to query against. * - * @see {@link QuerySpec} - * @see {@link OrmState} - * @see {@link OrmState} + * @see {@link Database.QuerySpec} + * @see {@link Database.QueryResult} + * @see {@link ORM.State} * * @return a {@link QueryResult} containing 0 to many {@link QueryResult.rows}. */ - query(querySpec: Database.QuerySpec, state: ORM.OrmState): Database.QueryResult; + query(querySpec: Database.QuerySpec, state: ORM.State): Database.QueryResult; /** * Apply an update to a given state. @@ -42,14 +42,13 @@ export interface Database, Tables = { [K * @param tx - a transaction for batches of operations. * @param state - the state to apply update to. * - * @see {@link UpdateSpec} - * @see {@link Transaction} - * @see {@link OrmState} + * @see {@link Database.UpdateSpec} + * @see {@link Database.Transaction} + * @see {@link ORM.State} * - * @return a {@link UpdateResult} containing 0 to many {@link QueryResult.rows}. + * @return a {@link Database.UpdateResult} containing 0 to many {@link Database.QueryResult.rows}. */ - - update(updateSpec: Database.UpdateSpec, tx: Database.Transaction, state: ORM.OrmState): Database.UpdateResult; + update(updateSpec: Database.UpdateSpec, tx: Database.Transaction, state: ORM.State): Database.UpdateResult; /** * Return a {@link Table} structure based on provided table name. @@ -57,77 +56,50 @@ export interface Database, Tables = { [K * * @return a {@link Table} instance matching given `tableName` or `undefined` if no such table exists. */ - describe(tableName: K): Tables[K]; + describe>(tableName: K): Table[K]>; } export namespace Database { - /** - * A type of {@link QueryClause}. - */ - type QueryType = typeof FILTER | typeof EXCLUDE | typeof ORDER_BY; - /** * A single `QueryClause`. * Multiple `QueryClause`s can be combined into a {@link Query}. */ interface QueryClause { - type: QueryType; + type: FILTER | EXCLUDE | ORDER_BY; payload: Payload; } - /** - * Query definition, contains target table and a collection of {@link QueryClause}. - */ + /** Query definition, contains target table and a collection of {@link QueryClause}. */ interface Query { table: string; clauses: QueryClause[]; } - /** - * Query wrapper definition, wraps {@link Query}. - */ + /** Query wrapper definition, wraps {@link Query}. */ interface QuerySpec { query: Query; } - /** - * Query result. - */ + /** Query result. */ interface QueryResult = {}> { rows: ReadonlyArray; } - /** - * A type of data update to perform. - */ - type UpdateType = typeof CREATE | typeof UPDATE | typeof DELETE; - - /** - * A status of data update operation. - */ - type UpdateStatus = typeof SUCCESS | typeof FAILURE; - - /** - * Data update definition - */ + /** Data update definition */ interface UpdateSpec { - action: UpdateType; + action: CREATE | UPDATE | DELETE; payload?: Payload; query?: Query; } - /** - * Data update result. - */ - interface UpdateResult, Payload extends object = {}> { - status: UpdateStatus; - state: ORM.OrmState; + /** Data update result. */ + interface UpdateResult { + status: SUCCESS | FAILURE; + state: ORM.State; payload: Payload; } - /** - * Transactions aggregate batches of operations. - */ + /** Transactions aggregate batches of operations. */ interface Transaction { batchToken: BatchToken; withMutations: boolean; @@ -136,22 +108,20 @@ export namespace Database { /** * Schema specification, required for default database creator. * - * @see {@link DatabaseCreator} - * @see {@link ModelTableOpts} * @see {@link Table} + * @see {@link Table.TableOpts} + * @see {@link ORM.Schema} */ - interface SchemaSpec> { - tables: { [K in keyof I]: Table.ModelTableOpts }; + interface SchemaSpec { + tables: { [K in keyof ORM.Registry]: Table.TableOpts[K]> }; } } - /** * Default database creation procedure handle. * - * @param schemaSpec - a {@link SchemaSpec} to built the {@link Database} from. + * @param schemaSpec - a {@link Database.SchemaSpec} to built the {@link Database} from. * * @return a {@Link Database} instance, ready for query and data update operation. */ -export function createDatabase>(schemaSpec: Database.SchemaSpec): Database; - +export function createDatabase(schemaSpec: Database.SchemaSpec): Database; export default createDatabase; diff --git a/types/redux-orm/db/Table.d.ts b/types/redux-orm/db/Table.d.ts index 26d04e5..a1c8861 100644 --- a/types/redux-orm/db/Table.d.ts +++ b/types/redux-orm/db/Table.d.ts @@ -1,11 +1,11 @@ -import Model, { AnyModel, FieldSpecKeys, IdType, Ref } from '../Model'; +import Model from '../Model'; import { ForeignKey, OneToOne } from '../index'; import { Field } from '../fields'; /** * Handles the underlying data structure for a {@link Model} class. */ -export class Table { +export class Table { /** * Creates a new {@link Table} instance. * @@ -18,7 +18,7 @@ export class Table { * map. * @param [userOpts.fields=DefaultTableOpts.fields] - mapping of field key to {@link Field} object */ - constructor(userOpts?: Table.ModelTableOpts); + constructor(userOpts?: Table.TableOpts); getEmptyState(): Table.TableState; } @@ -31,8 +31,8 @@ export namespace Table { maxId: MIdType extends number ? number : null | number; } - type TableIndexes = { - [K in FieldSpecKeys, OneToOne | ForeignKey>]: string; + type TableIndexes = { + [K in Model.FieldDescriptorKeys, OneToOne | ForeignKey>]: string; }; /** @@ -40,89 +40,64 @@ export namespace Table { * * Infers actual state of the ORM branch based on the {@link Model} class provided. */ - type TableState = { - readonly meta: DefaultMeta>>; + type TableState = { + readonly meta: DefaultMeta>>; readonly indexes: TableIndexes; - } & { readonly [K in ArrName]: ReadonlyArray>> } & + } & { readonly [K in ArrName]: ReadonlyArray>> } & { readonly [K in MapName]: { - readonly [K: string]: Ref>; + readonly [K: string]: Model.Ref>; }; }; - /** - * @internal - */ - type ExtractModelOption< - MClass extends typeof AnyModel, - K extends keyof TableOpts, - DefaultValue extends string - > = MClass['options'] extends () => { [P in K]: infer R } - ? R extends string - ? R - : DefaultValue - : MClass['options'] extends { [P in K]: infer R } - ? R extends string - ? R - : DefaultValue - : DefaultValue; - /** * Model idAttribute option extraction helper. * * Falls back to `'id'` if not specified explicitly via {@link Model.options}. */ - type IdAttribute = ExtractModelOption; + type IdAttribute = MClass['options'] extends () => { idAttribute: infer R } + ? R + : MClass['options'] extends { idAttribute: infer R } + ? R + : 'id'; /** * Model arrName option extraction helper. * * Falls back to `'items'` if not specified explicitly via {@link Model.options}. */ - type ArrName = ExtractModelOption; + type ArrName = (MClass['options'] extends () => { arrName: infer R } + ? R + : MClass['options'] extends { arrName: infer R } + ? R + : 'items') extends infer U + ? U extends string + ? U + : never + : never; /** * Model mapName option extraction helper. * * Falls back to `'itemsById'` if not specified explicitly via {@link Model.options}. */ - type MapName = ExtractModelOption; + type MapName = (MClass['options'] extends () => { mapName: infer R } + ? R + : MClass['options'] extends { mapName: infer R } + ? R + : 'itemsById') extends infer U + ? U extends string + ? U + : never + : never; /** - * Unbox {@link Model#options} or fallback to default for others. - * * @internal */ - interface ModelTableOpts { + interface TableOpts { readonly idAttribute: IdAttribute; readonly arrName: ArrName; readonly mapName: MapName; readonly fields: MClass['fields']; } - - /** - * {@link TableOpts} used for {@link Table} customization. - * - * Supplied via {@link Model#options}. - * - * If no customizations were provided, the table uses following default options: - *
- * ```typescript - * { - * idAttribute: 'id', - * arrName: 'items', - * mapName: 'itemsById' - * } - * ``` - *
- * @see {@link Model} - * @see {@link Model#options} - * @see {@link OrmState} - */ - interface TableOpts { - readonly idAttribute?: string; - readonly arrName?: string; - readonly mapName?: string; - readonly fields?: { [K: string]: Field }; - } } diff --git a/types/redux-orm/db/index.d.ts b/types/redux-orm/db/index.d.ts index 4f6bd01..d0435f7 100644 --- a/types/redux-orm/db/index.d.ts +++ b/types/redux-orm/db/index.d.ts @@ -1,5 +1,5 @@ -import createDatabase = require('./Database'); +import { createDatabase, Database } from './Database'; -export * from './Database'; export { Table } from './Table'; +export { createDatabase, Database }; export default createDatabase; diff --git a/types/redux-orm/fields.d.ts b/types/redux-orm/fields.d.ts index b8954ee..3c3c4ed 100644 --- a/types/redux-orm/fields.d.ts +++ b/types/redux-orm/fields.d.ts @@ -45,34 +45,14 @@ export class ManyToMany extends RelationalField { ['type']: 'many'; } -export interface AttrCreator { - (): Attribute; - (opts: AttributeOpts): AttributeWithDefault; -} - -export interface FkCreator { - (toModelName: string, relatedName?: string): ForeignKey; - (opts: RelationalFieldOpts): ForeignKey; -} - -export interface ManyCreator { - (toModelName: string, relatedName?: string): ManyToMany; - (opts: RelationalFieldOpts): ManyToMany; -} +export function attr(opts: AttributeOpts): AttributeWithDefault; +export function attr(): Attribute; -export interface OneToOneCreator { - (toModelName: string, relatedName?: string): OneToOne; - (opts: RelationalFieldOpts): OneToOne; -} - -export const attr: AttrCreator; - -export const oneToOne: OneToOneCreator; +export function fk(toModelName: string, relatedName?: string): ForeignKey; +export function fk(opts: RelationalFieldOpts): ForeignKey; -export const fk: FkCreator; +export function many(toModelName: string, relatedName?: string): ManyToMany; +export function many(opts: RelationalFieldOpts): ManyToMany; -export const many: ManyCreator; - -export interface FieldSpecMap { - [K: string]: Attribute | ForeignKey | ManyToMany | OneToOne; -} +export function oneToOne(toModelName: string, relatedName?: string): OneToOne; +export function oneToOne(opts: RelationalFieldOpts): OneToOne; diff --git a/types/redux-orm/helpers.d.ts b/types/redux-orm/helpers.d.ts index 196c6b7..31d34d8 100644 --- a/types/redux-orm/helpers.d.ts +++ b/types/redux-orm/helpers.d.ts @@ -1,9 +1,14 @@ +/** Primitive value */ +export type Primitive = (number | string | boolean | undefined) | Array; + +/** Plain JS object */ +export interface PlainObject { + [K: string]: Serializable; +} + +/** Serializable value: a {@link Primitive}, a {@link PlainObject} or an `Array` */ +export type Serializable = Primitive | PlainObject | PlainObject[]; + export type PickByValue = Pick; export type OptionalKeys = { [K in keyof T]-?: {} extends Pick ? K : never }[keyof T]; - -export type KnownKeys = { - [K in keyof T]: string extends K ? never : number extends K ? never : K; -} extends { [_ in keyof T]: infer U } - ? U - : never; diff --git a/types/redux-orm/index.d.ts b/types/redux-orm/index.d.ts index 17e43f6..d1b47ae 100644 --- a/types/redux-orm/index.d.ts +++ b/types/redux-orm/index.d.ts @@ -5,60 +5,12 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 3.5 -import { ORM } from './ORM'; -import Model, { - CreateProps, - CustomInstanceProps, - IdKey, - IdOrModelLike, - IdType, - ModelField, - ModelFieldMap, - Ref, - RefPropOrSimple, - SessionBoundModel, - UpdateProps, - UpsertProps -} from './Model'; -import QuerySet, { MutableQuerySet } from './QuerySet'; -import { OrmSession } from './Session'; -import { createDatabase } from './db'; -import { attr, Attribute, FieldSpecMap, fk, ForeignKey, many, ManyToMany, OneToOne, oneToOne } from './fields'; -import { createReducer, createSelector, defaultUpdater, ORMReducer, ORMSelector } from './redux'; - -export { - FieldSpecMap, - RefPropOrSimple, - ModelFieldMap, - CustomInstanceProps, - UpsertProps, - CreateProps, - UpdateProps, - ModelField, - OrmSession as Session, - MutableQuerySet, - createDatabase, - createSelector, - createReducer, - defaultUpdater, - ORMSelector, - ORMReducer, - IdOrModelLike, - Ref, - SessionBoundModel, - IdKey, - IdType, - ORM, - Model, - QuerySet, - Attribute, - OneToOne, - ForeignKey, - ManyToMany, - attr, - oneToOne, - fk, - many -}; +export { ORM } from './ORM'; +export { Model } from './Model'; +export { OrmSession as Session } from './Session'; +export { createDatabase } from './db'; +export { attr, Attribute, fk, ForeignKey, many, ManyToMany, OneToOne, oneToOne } from './fields'; +export { createReducer, createSelector } from './redux'; -export default Model; +import QuerySet, { MutableQuerySet } from './QuerySet'; +export { MutableQuerySet, QuerySet }; diff --git a/types/redux-orm/redux-orm-tests.ts b/types/redux-orm/redux-orm-tests.ts index ba0f6bb..0269d12 100644 --- a/types/redux-orm/redux-orm-tests.ts +++ b/types/redux-orm/redux-orm-tests.ts @@ -1,16 +1,4 @@ -import { - attr, - createSelector as createOrmSelector, - fk, - IdKey, - IdType, - many, - Model, - MutableQuerySet, - ORM, - QuerySet, - Ref -} from 'redux-orm'; +import { createSelector as createOrmSelector, attr, fk, many, Model, MutableQuerySet, ORM, QuerySet } from 'redux-orm'; interface CreateBookAction { type: 'CREATE_BOOK'; @@ -24,14 +12,14 @@ interface DeleteBookAction { type RootAction = CreateBookAction | DeleteBookAction; -interface BookFields { +interface Book { title: string; coverArt: string; publisher: Publisher; authors: MutableQuerySet; } -class Book extends Model { +class Book extends Model { static modelName = 'Book' as const; static fields = { title: attr(), @@ -48,7 +36,7 @@ class Book extends Model { book.create(action.payload); break; case 'DELETE_BOOK': - book.filter(book => book.title === action.payload.title).delete(); + book.withId(action.payload.title)!.delete(); break; default: break; @@ -56,7 +44,7 @@ class Book extends Model { } } -interface PersonFields { +interface Person { id: string; firstName: string; lastName: string; @@ -64,7 +52,7 @@ interface PersonFields { books: MutableQuerySet; } -class Person extends Model { +class Person extends Model { static modelName = 'Person' as const; static fields = { id: attr(), @@ -74,14 +62,14 @@ class Person extends Model { }; } -interface AuthorshipFields { +interface Authorship { id: number; year?: number; book: Book; author: Person; } -class Authorship extends Model { +class Authorship extends Model { static modelName = 'Authorship' as const; static fields = { year: attr(), @@ -90,13 +78,13 @@ class Authorship extends Model { }; } -interface PublisherFields { +interface Publisher { index: number; name: string; books: QuerySet; } -class Publisher extends Model { +class Publisher extends Model { static modelName = 'Publisher' as const; static fields = { index: attr(), @@ -107,9 +95,7 @@ class Publisher extends Model { }; } -const schema = { Book, Authorship, Person, Publisher }; - -type Schema = typeof schema; +type Schema = [typeof Book, typeof Authorship, typeof Person, typeof Publisher]; // create ORM instance and register { Book, Publisher, Person, Authorship } schema const ormFixture = () => { @@ -197,7 +183,7 @@ const sessionFixture = () => { /** Upsert requires id to be provided */ Book.upsert({ publisher: 1 }); // $ExpectError - // $ExpectType SessionBoundModel> + // $ExpectType Book & Pick<{ title: string; publisher: number; }, never> Book.upsert({ title: 'B1', publisher: 1 }); /* Incompatible property types: */ @@ -231,8 +217,7 @@ const sessionFixture = () => { // restriction of allowed ORM.register args (() => { - const incompleteSchema = { Book, Authorship, Person }; - const orm = new ORM(); + const orm = new ORM<[typeof Book, typeof Authorship, typeof Person]>(); orm.register(Book, Authorship, Person, Publisher); // $ExpectError })(); @@ -257,9 +242,8 @@ const sessionFixture = () => { // IdKey and IdType mapped types support for valid identifier configurations (() => { - type ExtractId = [IdKey, IdType]; + type ExtractId = [Model.IdKey, Model.IdType]; - type ImplicitDefault = ExtractId; // $ExpectType ["id", number] type CustomKey = ExtractId; // $ExpectType ["index", number] type CustomType = ExtractId; // $ExpectType ["id", string] type CustomKeyAndType = ExtractId; // $ExpectType ["title", string] @@ -293,7 +277,7 @@ const sessionFixture = () => { (() => { const orm = ormFixture(); - type StateType = ORM.OrmState; + type StateType = ORM.State; return (state: StateType, action: CreateBookAction): StateType => { const session = orm.session(state); @@ -341,7 +325,7 @@ const sessionFixture = () => { // selectors (() => { // test fixture, use reselect.createSelector in production code - const createSelector = , Result extends any>( + const createSelector = , Result extends any>( param1Creator: (state: S) => OS, combiner: (param1: OS) => Result ): ((state: S) => Result) => state => combiner(param1Creator(state)); @@ -351,20 +335,20 @@ const sessionFixture = () => { const ormSelector = createOrmSelector(orm, session => session.Book.all().toRefArray()[0]); interface RootState { - db: ORM.OrmState; + db: ORM.State; } - const selector = createSelector, Ref>( + const selector = createSelector, Model.Ref>( ({ db }) => db, ormSelector ); - createSelector, Ref>( + selector({ db: orm.getEmptyState() }); // $ExpectType Ref + + return createSelector, Model.Ref>( ({ db }) => db, ormSelector // $ExpectError ); - - selector({ db: orm.getEmptyState() }); // $ExpectType Ref })(); // advanced selectors @@ -374,10 +358,10 @@ const sessionFixture = () => { interface RootState { foo: number; bar: string; - db: ORM.OrmState; + db: ORM.State; } - type TestSelector = (state: RootState) => Ref; + type TestSelector = (state: RootState) => Model.Ref; const selector0 = createOrmSelector(orm, s => s.db, session => session.Book.first()!.ref) as TestSelector; diff --git a/types/redux-orm/redux.d.ts b/types/redux-orm/redux.d.ts index bd0c7c3..7da9357 100644 --- a/types/redux-orm/redux.d.ts +++ b/types/redux-orm/redux.d.ts @@ -1,87 +1,87 @@ import { ORM } from './ORM'; import { OrmSession } from './Session'; -export interface ORMReducer { - (state: ORM.OrmState | undefined, action: TAction): ORM.OrmState; +export interface ORMReducer { + (state: ORM.State | undefined, action: TAction): ORM.State; } -export type defaultUpdater = ( - session: OrmSession, +export type defaultUpdater = ( + session: OrmSession, action: TAction ) => void; -export function createReducer( - orm: ORM, - updater?: defaultUpdater -): ORMReducer; +export function createReducer( + orm: ORM, + updater?: defaultUpdater +): ORMReducer; export type Selector = (state: S) => R; -export interface ORMSelector { - (session: OrmSession, ...args: Args): R; +export interface ORMSelector { + (session: OrmSession, ...args: Args): R; } -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, selector1: Selector, selector2: Selector, selector3: Selector, selector4: Selector, selector5: Selector, selector6: Selector, - ormSelector: ORMSelector + ormSelector: ORMSelector ): Selector; -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, selector1: Selector, selector2: Selector, selector3: Selector, selector4: Selector, selector5: Selector, - ormSelector: ORMSelector + ormSelector: ORMSelector ): Selector; -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, selector1: Selector, selector2: Selector, selector3: Selector, selector4: Selector, - ormSelector: ORMSelector + ormSelector: ORMSelector ): Selector; -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, selector1: Selector, selector2: Selector, selector3: Selector, - ormSelector: ORMSelector + ormSelector: ORMSelector ): Selector; -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, selector1: Selector, selector2: Selector, - ormSelector: ORMSelector + ormSelector: ORMSelector ): Selector; -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, selector1: Selector, - ormSelector: ORMSelector + ormSelector: ORMSelector ): Selector; -export function createSelector( - orm: ORM, - OrmStateSelector: Selector>, - ormSelector: ORMSelector +export function createSelector( + orm: ORM, + OrmStateSelector: Selector>, + ormSelector: ORMSelector ): Selector; -export function createSelector(orm: ORM, ormSelector: ORMSelector): Selector, R>; +export function createSelector(orm: ORM, ormSelector: ORMSelector): Selector, R>;