mirror of
https://github.com/FlipsideCrypto/DefinitelyTyped.git
synced 2026-02-06 10:56:53 +00:00
[@types/redux-orm] typings for redux-orm@0.13.3 (#35609)
This commit is contained in:
parent
806e23a198
commit
ca0f882668
545
types/redux-orm/Model.d.ts
vendored
Normal file
545
types/redux-orm/Model.d.ts
vendored
Normal file
@ -0,0 +1,545 @@
|
||||
import { ModelTableOpts, TableOpts } from './db';
|
||||
import { IdAttribute } from './db/Table';
|
||||
import { AttributeWithDefault, FieldSpecMap, ForeignKey, ManyToMany, OneToOne } from './fields';
|
||||
import { Optional, OptionalKeys, Overwrite, PickByValue } from './helpers';
|
||||
import { IdOrModelLike, ModelField } from './index';
|
||||
import QuerySet, { LookupSpec, MutableQuerySet, SortIteratee, SortOrder } from './QuerySet';
|
||||
import { OrmSession } from './Session';
|
||||
|
||||
/**
|
||||
* 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[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Object restricted to serializable properties only
|
||||
*/
|
||||
export interface SerializableMap {
|
||||
[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 = MutableQuerySet | 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 a map containing an {IdAttribute: IdType} pair,
|
||||
* where IdAttribute and IdType match respective Model property key and type
|
||||
*/
|
||||
export type IdOrModelLike<M extends Model> = IdType<M> | IdEntry<M>;
|
||||
|
||||
/**
|
||||
* The heart of an ORM, the data model.
|
||||
*
|
||||
* The fields you specify to the Model will be used to generate
|
||||
* a schema to the database, related property accessors, and
|
||||
* possibly through models.
|
||||
*
|
||||
* In each {@link Session} you instantiate from an {@link ORM} instance,
|
||||
* you will receive a session-specific subclass of this Model. The methods
|
||||
* you define here will be available to you in sessions.
|
||||
*
|
||||
* An instance of {@link Model} represents a record in the database, though
|
||||
* it is possible to generate multiple instances from the same record in the database.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
export default class Model<MClass extends typeof AnyModel = any, Fields extends ModelFieldMap = any> {
|
||||
/**
|
||||
* A string constant identifying specific Model, necessary to retain the shape of state and relations through transpilation steps
|
||||
*/
|
||||
static modelName: string;
|
||||
|
||||
/**
|
||||
* Model field descriptors.
|
||||
* @see {@link Attribute}
|
||||
* @see {@link OneToOne}
|
||||
* @see {@link ForeignKey}
|
||||
* @see {@link ManyToMany}
|
||||
*/
|
||||
static fields: FieldSpecMap;
|
||||
|
||||
/**
|
||||
* Returns the options object passed to the database for the table that represents
|
||||
* this Model class.
|
||||
*
|
||||
* Returns an empty object by default, which means the database
|
||||
* will use default options. You can either override this function to return the options
|
||||
* you want to use, or assign the options object as a static property of the same name to the
|
||||
* Model class.
|
||||
*
|
||||
* @return the options object passed to the database for the table
|
||||
* representing this Model class.
|
||||
*/
|
||||
static options: { (): TableOpts } | TableOpts;
|
||||
|
||||
/**
|
||||
* The key of Model's identifier property
|
||||
*/
|
||||
static readonly idAttribute: string;
|
||||
/**
|
||||
* {@link QuerySet} class associated with this Model class.
|
||||
*
|
||||
* Defaults to base {@link QuerySet}
|
||||
*/
|
||||
static querySetClass: typeof QuerySet;
|
||||
|
||||
/**
|
||||
* @see {@link Model.getQuerySet}
|
||||
*/
|
||||
static readonly query: QuerySet;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
readonly ref: Ref<InstanceType<MClass>>;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Model specific reducer function.
|
||||
*
|
||||
* An alternative to standalone reducer function.
|
||||
*
|
||||
* @see {@link createReducer}
|
||||
*
|
||||
* @param action - store-dispatched action instance
|
||||
* @param modelType - a {@link ModelType} parametrized with a
|
||||
* {@link Model} type that the reducer is being attached to.
|
||||
* @param session - an optional parameter, can be used for querying other Models (mutations are not supported)
|
||||
*/
|
||||
static reducer(action: any, modelType: ModelType<any>, session: OrmSession<any>): void;
|
||||
|
||||
/**
|
||||
* Creates a new record in the database, instantiates a {@link Model} and returns it.
|
||||
*
|
||||
* If you pass values for many-to-many fields, instances are created on the through
|
||||
* model as well.
|
||||
*
|
||||
* @param userProps - the new {@link Model}'s properties.
|
||||
* @return a new {@link SessionBoundModel} instance.
|
||||
*/
|
||||
static create<M extends AnyModel, TProps extends CreateProps<M>>(userProps: TProps): SessionBoundModel<M, TProps>;
|
||||
|
||||
/**
|
||||
* Creates a new or update existing record in the database, instantiates a {@link Model} and returns it.
|
||||
*
|
||||
* If you pass values for many-to-many fields, instances are created on the through
|
||||
* model as well.
|
||||
*
|
||||
* @param userProps - the upserted {@link Model}'s properties.
|
||||
* @return a {@link SessionBoundModel} instance.
|
||||
*/
|
||||
static upsert<M extends AnyModel, TProps extends UpsertProps<M>>(userProps: TProps): SessionBoundModel<M, TProps>;
|
||||
|
||||
/**
|
||||
* Gets the {@link Model} instance that matches properties in `lookupObj`.
|
||||
* Throws an error if {@link Model} if multiple records match
|
||||
* the properties.
|
||||
*
|
||||
* @param lookupObj - 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`.
|
||||
*/
|
||||
static get<M extends AnyModel, TProps extends LookupSpec<M>>(
|
||||
lookupObj: TProps
|
||||
): SessionBoundModel<M, TProps> | null;
|
||||
|
||||
/**
|
||||
* 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`
|
||||
*/
|
||||
static withId<M extends AnyModel>(id: IdType<M>): SessionBoundModel<M> | null;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @since 0.11.0
|
||||
*/
|
||||
static idExists(id: string | number): boolean;
|
||||
|
||||
/**
|
||||
* @return A string representation of this {@link Model} class.
|
||||
*/
|
||||
static toString(): string;
|
||||
|
||||
/**
|
||||
* Manually mark individual instances as accessed.
|
||||
* This allows invalidating selector memoization within mutable sessions.
|
||||
*
|
||||
* @param ids - Array of primary key values
|
||||
*/
|
||||
static markAccessed(ids: Array<string | number>): void;
|
||||
|
||||
/**
|
||||
* Manually mark this model's table as scanned.
|
||||
* This allows invalidating selector memoization within mutable sessions.
|
||||
*
|
||||
*/
|
||||
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`.
|
||||
*/
|
||||
static getQuerySet(): QuerySet;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.all}
|
||||
*/
|
||||
static all<M extends AnyModel>(this: ModelType<M>): QuerySet<M>;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.at}
|
||||
*/
|
||||
static at(index: number): SessionBoundModel | undefined;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.first}
|
||||
*/
|
||||
static first(): SessionBoundModel | undefined;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.last}
|
||||
*/
|
||||
static last(): SessionBoundModel | undefined;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.update}
|
||||
*/
|
||||
static update(props: UpdateProps<Model>): void;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.filter}
|
||||
*/
|
||||
static filter(props: LookupSpec<Model>): QuerySet;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.exclude}
|
||||
*/
|
||||
static exclude(props: LookupSpec<Model>): QuerySet;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.orderBy}
|
||||
*/
|
||||
static orderBy(iteratees: ReadonlyArray<SortIteratee<Model>>, orders?: ReadonlyArray<SortOrder>): QuerySet;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.count}
|
||||
*/
|
||||
static count(): number;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.exists}
|
||||
*/
|
||||
static exists(): boolean;
|
||||
|
||||
/**
|
||||
* @see {@link QuerySet.delete}
|
||||
*/
|
||||
static delete(): void;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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(): string | number;
|
||||
|
||||
/**
|
||||
* @return A string representation of this {@link Model} instance.
|
||||
*/
|
||||
toString(): string;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
equals(otherModel: Model | SessionBoundModel): boolean;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
set<K extends string>(propertyName: K, value: RefPropOrSimple<InstanceType<MClass>, K>): void;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
update(userMergeObj: UpdateProps<InstanceType<MClass>>): 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Model wildcard type.
|
||||
*/
|
||||
export class AnyModel extends Model {}
|
||||
|
||||
/**
|
||||
* {@link Model#upsert} argument type
|
||||
*
|
||||
* Relations can be provided in a flexible manner for both many-to-many and foreign key associations
|
||||
* @see {@link IdOrModelLike}
|
||||
*/
|
||||
export type UpsertProps<M extends Model> = Overwrite<Partial<CreateProps<M>>, Required<IdEntry<M>>>;
|
||||
|
||||
/**
|
||||
* {@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<M extends Model> = Omit<UpsertProps<M>, IdKey<M>>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type CustomInstanceProps<M extends AnyModel, Props extends object> = PickByValue<
|
||||
Omit<Props, Extract<keyof Props, keyof ModelFields<M>>>,
|
||||
Serializable
|
||||
>;
|
||||
|
||||
/**
|
||||
* Model id property key extraction helper.
|
||||
*
|
||||
* Falls back to `'id'` if not specified explicitly via {@link Model.options}.
|
||||
*/
|
||||
export type IdKey<M extends AnyModel> = IdAttribute<ModelClass<M>>;
|
||||
|
||||
/**
|
||||
* Model id property type extraction helper.
|
||||
*
|
||||
* Falls back to `number` if not specified explicitly via {@link Model.options}.
|
||||
*/
|
||||
export type IdType<M extends Model> = IdKey<M> extends infer U
|
||||
? U extends keyof ModelFields<M>
|
||||
? ModelFields<M>[U] extends string | number
|
||||
? ModelFields<M>[U]
|
||||
: never
|
||||
: number
|
||||
: number;
|
||||
|
||||
/**
|
||||
* A single entry map representing IdKey: IdType property of supplied {@link Model}.
|
||||
*/
|
||||
export type IdEntry<M extends Model> = { [K in IdKey<M>]: IdType<M> };
|
||||
|
||||
/**
|
||||
* Type of {@link Model.ref} / database entry for a particular Model type
|
||||
*/
|
||||
export type Ref<M extends Model> = {
|
||||
[K in keyof RefFields<M>]: ModelFields<M>[K] extends AnyModel ? IdType<ModelFields<M>[K]> : RefFields<M>[K]
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<M extends Model, K extends string> = K extends keyof RefFields<M>
|
||||
? Ref<M>[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 extends Model = any, InstanceProps extends object = {}> = M &
|
||||
{ [K in keyof ModelFields<M>]: SessionBoundModelField<M, K> } &
|
||||
CustomInstanceProps<M, InstanceProps>;
|
||||
|
||||
/**
|
||||
* Static side of a particular {@link Model} with member signatures narrowed to provided {@link Model} type
|
||||
*
|
||||
* @template M a model type narrowing static {@link Model} member signatures.
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
export interface ModelType<M extends AnyModel> extends QuerySet<M> {
|
||||
new (props: ModelFields<M>): SessionBoundModel<M>;
|
||||
|
||||
options: ModelTableOpts<ModelClass<M>>;
|
||||
|
||||
modelName: ModelClass<M>['modelName'];
|
||||
|
||||
fields: ModelClass<M>['fields'];
|
||||
|
||||
/**
|
||||
* @see {@link Model#idExists}
|
||||
*/
|
||||
idExists(id: IdType<M>): boolean;
|
||||
|
||||
/**
|
||||
* @see {@link Model#withId}
|
||||
*/
|
||||
withId(id: IdType<M>): SessionBoundModel<M> | null;
|
||||
|
||||
/**
|
||||
* @see {@link Model#get}
|
||||
*/
|
||||
get<TLookup extends LookupSpec<M>>(lookupSpec: TLookup): SessionBoundModel<M, TLookup> | null;
|
||||
|
||||
/**
|
||||
* @see {@link Model#create}
|
||||
*/
|
||||
create<TProps extends CreateProps<M>>(props: TProps): SessionBoundModel<M, TProps>;
|
||||
|
||||
/**
|
||||
* @see {@link Model#upsert}
|
||||
*/
|
||||
upsert<TProps extends UpsertProps<M>>(props: TProps): SessionBoundModel<M, TProps>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type ModelClass<M extends AnyModel> = ReturnType<M['getClass']>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type ModelFields<M extends Model> = [ConstructorParameters<ModelClass<M>>] extends [[infer U]]
|
||||
? U extends ModelFieldMap
|
||||
? U
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type FieldSpecKeys<M extends AnyModel, TField> = keyof PickByValue<ModelClass<M>['fields'], TField>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type RefFields<M extends AnyModel, K extends keyof ModelFields<M> = keyof ModelFields<M>> = Omit<
|
||||
ModelFields<M>,
|
||||
Extract<K, FieldSpecKeys<M, ManyToMany>>
|
||||
>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type SessionBoundModelField<M extends AnyModel, K extends keyof ModelFields<M>> = ModelFields<
|
||||
M
|
||||
>[K] extends AnyModel
|
||||
? SessionBoundModel<ModelFields<M>[K]>
|
||||
: ModelFields<M>[K];
|
||||
|
||||
/**
|
||||
* {@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,
|
||||
RFields extends Required<ModelFields<M>> = Required<ModelFields<M>>
|
||||
> = Optional<
|
||||
{
|
||||
[K in keyof ModelFields<M>]: {
|
||||
[P in K]: RFields[P] extends MutableQuerySet<infer RM>
|
||||
? ReadonlyArray<IdOrModelLike<RM>>
|
||||
: (RFields[P] extends QuerySet
|
||||
? never
|
||||
: RFields[P] extends AnyModel
|
||||
? (P extends FieldSpecKeys<M, OneToOne | ForeignKey> ? IdOrModelLike<RFields[P]> : never)
|
||||
: RFields[P])
|
||||
}[K]
|
||||
},
|
||||
OptionalCreatePropsKeys<M>
|
||||
>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type OptionalCreatePropsKeys<M extends Model> = IdType<M> extends number
|
||||
? (IdKey<M> | OptionalKeys<ModelFields<M>> | FieldSpecKeys<M, AttributeWithDefault>)
|
||||
: (OptionalKeys<ModelFields<M>> | FieldSpecKeys<M, AttributeWithDefault>);
|
||||
115
types/redux-orm/ORM.d.ts
vendored
Normal file
115
types/redux-orm/ORM.d.ts
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
import { Database, DatabaseCreator, TableState } from './db';
|
||||
import { AnyModel } from './Model';
|
||||
import Session, { OrmSession } from './Session';
|
||||
|
||||
/**
|
||||
* A `{typeof Model[modelName]: typeof Model}` map defining:
|
||||
*
|
||||
* - database schema
|
||||
* - {@link Session} bound Model classes
|
||||
* - ORM branch state type
|
||||
*/
|
||||
export type IndexedModelClasses<
|
||||
T extends { [k in keyof T]: typeof AnyModel },
|
||||
K extends keyof T = Extract<keyof T, T[keyof T]['modelName']>
|
||||
> = { [k in K]: T[K] };
|
||||
|
||||
/**
|
||||
* A mapped type capable of inferring ORM branch state type based on schema {@link Model}s.
|
||||
*/
|
||||
export type OrmState<MClassMap extends IndexedModelClasses<any>> = { [K in keyof MClassMap]: TableState<MClassMap[K]> };
|
||||
|
||||
/**
|
||||
* ORM instantiation opts.
|
||||
*
|
||||
* Enables customization of database creation.
|
||||
*/
|
||||
export interface ORMOpts {
|
||||
createDatabase: DatabaseCreator;
|
||||
}
|
||||
|
||||
/**
|
||||
* ORM - the Object Relational Mapper.
|
||||
*
|
||||
* Use instances of this class to:
|
||||
*
|
||||
* - Register your {@link Model} classes using {@link ORM#register}
|
||||
* - Get the empty state for the underlying database with {@link ORM#getEmptyState}
|
||||
* - Start an immutable database session with {@link ORM#session}
|
||||
* - Start a mutating database session with {@link ORM#mutableSession}
|
||||
*
|
||||
* Internally, this class handles generating a schema specification from models
|
||||
* to the database.
|
||||
*/
|
||||
export class ORM<I extends IndexedModelClasses<any>, ModelNames extends keyof I = keyof I> {
|
||||
/**
|
||||
* Creates a new ORM instance.
|
||||
*/
|
||||
constructor(opts?: ORMOpts);
|
||||
|
||||
/**
|
||||
* Registers a {@link Model} class to the ORM.
|
||||
*
|
||||
* If the model has declared any ManyToMany fields, their
|
||||
* through models will be generated and registered with
|
||||
* this call, unless a custom through model has been specified.
|
||||
*
|
||||
* @param model - a {@link Model} class to register
|
||||
*/
|
||||
register(...model: ReadonlyArray<I[ModelNames]>): void;
|
||||
|
||||
/**
|
||||
* Gets a {@link Model} class by its name from the registry.
|
||||
*
|
||||
* @param modelName - the name of the {@link Model} class to get
|
||||
*
|
||||
* @throws If {@link Model} class is not found.
|
||||
*
|
||||
* @return the {@link Model} class, if found
|
||||
*/
|
||||
get<K extends ModelNames>(modelName: K): I[K];
|
||||
|
||||
/**
|
||||
* Returns the empty database state.
|
||||
*
|
||||
* @see {@link OrmState}
|
||||
*
|
||||
* @return empty state
|
||||
*/
|
||||
getEmptyState(): OrmState<I>;
|
||||
|
||||
/**
|
||||
* Begins an immutable database session.
|
||||
*
|
||||
* @see {@link OrmState}
|
||||
* @see {@link SessionType}
|
||||
*
|
||||
* @param state - the state the database manages
|
||||
*
|
||||
* @return a new {@link Session} instance
|
||||
*/
|
||||
session(state: OrmState<I>): OrmSession<I>;
|
||||
|
||||
/**
|
||||
* Begins an mutable database session.
|
||||
*
|
||||
* @see {@link OrmState}
|
||||
* @see {@link SessionType}
|
||||
*
|
||||
* @param state - the state the database manages
|
||||
*
|
||||
* @return a new {@link Session} instance
|
||||
*/
|
||||
mutableSession(state: OrmState<I>): OrmSession<I>;
|
||||
|
||||
/**
|
||||
* Acquire database reference.
|
||||
*
|
||||
* If no database exists, an instance is created using either default or supplied implementation of {@link DatabaseCreator}.
|
||||
*
|
||||
* @return A {@link Database} instance structured according to registered schema.
|
||||
*/
|
||||
getDatabase(): Database<I>;
|
||||
}
|
||||
|
||||
export default ORM;
|
||||
264
types/redux-orm/QuerySet.d.ts
vendored
Normal file
264
types/redux-orm/QuerySet.d.ts
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
import { QueryClause } from './db';
|
||||
import Model, {
|
||||
AnyModel,
|
||||
CustomInstanceProps,
|
||||
IdOrModelLike,
|
||||
ModelClass,
|
||||
Ref,
|
||||
SessionBoundModel,
|
||||
UpdateProps
|
||||
} from './Model';
|
||||
|
||||
/**
|
||||
* Optional ordering direction.
|
||||
*
|
||||
* {@see QuerySet.orderBy}
|
||||
*/
|
||||
export type SortOrder = 'asc' | 'desc' | true | false;
|
||||
|
||||
/**
|
||||
* Ordering clause.
|
||||
*
|
||||
* Either a key of SessionBoundModel or a evaluator function accepting plain object Model representation stored in the database.
|
||||
*
|
||||
* {@see QuerySet.orderBy}
|
||||
*/
|
||||
export type SortIteratee<M extends Model> = keyof Ref<M> | { (row: Ref<M>): any };
|
||||
|
||||
/**
|
||||
* Lookup clause as an object specifying props to match with plain object Model representation stored in the database.
|
||||
* {@see QuerySet.exclude}
|
||||
* {@see QuerySet.filter}
|
||||
*/
|
||||
export type LookupProps<M extends Model> = Partial<Ref<M>>;
|
||||
|
||||
/**
|
||||
* Lookup clause as predicate accepting plain object Model representation stored in the database.
|
||||
* {@see QuerySet.exclude}
|
||||
* {@see QuerySet.filter}
|
||||
*/
|
||||
export type LookupPredicate<M extends Model> = (row: Ref<M>) => boolean;
|
||||
|
||||
/**
|
||||
* A union of lookup clauses.
|
||||
* {@see QuerySet.exclude}
|
||||
* {@see QuerySet.filter}
|
||||
*/
|
||||
export type LookupSpec<M extends Model> = LookupProps<M> | LookupPredicate<M>;
|
||||
|
||||
/**
|
||||
* A lookup query result.
|
||||
*
|
||||
* May contain additional properties in case {@link LookupProps} clause had been supplied.
|
||||
* {@see QuerySet.exclude}
|
||||
* {@see QuerySet.filter}
|
||||
*/
|
||||
export type LookupResult<M extends Model, TLookup extends LookupSpec<M>> = TLookup extends LookupPredicate<M>
|
||||
? QuerySet<M>
|
||||
: QuerySet<M, CustomInstanceProps<M, LookupProps<M>>>;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* `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).
|
||||
* <p>
|
||||
*
|
||||
* @example queries are built lazily
|
||||
* const qs = Book.all()
|
||||
* .filter(book => book.releaseYear > 1999)
|
||||
* .orderBy('name')
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* After the query is executed, the resulting set is cached in the QuerySet instance.
|
||||
*
|
||||
* QuerySet instances return copies, so chaining filters doesn't
|
||||
* mutate the previous instances.
|
||||
*
|
||||
* @template M type of {@link Model} instances returned by QuerySet's methods.
|
||||
* @template InstanceProps additional properties available on QuerySet's elements.
|
||||
*/
|
||||
export default class QuerySet<M extends AnyModel = any, InstanceProps extends object = {}> {
|
||||
/**
|
||||
* Creates a `QuerySet`. The constructor is mainly for internal use;
|
||||
* Access QuerySet instances from {@link Model}.
|
||||
*
|
||||
* @param modelClass - the {@link Model} class of objects in this QuerySet.
|
||||
* @param clauses - query clauses needed to evaluate the set.
|
||||
* @param [opts] - additional options
|
||||
*/
|
||||
constructor(modelClass: ModelClass<M>, clauses: QueryClause[], opts?: object);
|
||||
|
||||
/**
|
||||
* 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<Book> {
|
||||
* static currentYear = 2019
|
||||
* unreleased(): QuerySet<Book> {
|
||||
* 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<typeof schema>();
|
||||
* orm.register(Book);
|
||||
* const session = orm.session(orm.getEmptyState());
|
||||
* // use shared method
|
||||
* const unreleased = customQs.unreleased();
|
||||
*/
|
||||
static addSharedMethod(methodName: string): void;
|
||||
|
||||
/**
|
||||
* Returns a new {@link QuerySet} instance with the same entities.
|
||||
* @return a new QuerySet with the same entities.
|
||||
*/
|
||||
all(): QuerySet<M, InstanceProps>;
|
||||
|
||||
/**
|
||||
* Returns the {@link SessionBoundModel} 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
|
||||
* `index` in the {@link QuerySet} instance,
|
||||
* or undefined if the index is out of bounds.
|
||||
*/
|
||||
at(index: number): SessionBoundModel<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.
|
||||
*/
|
||||
first(): SessionBoundModel<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.
|
||||
*/
|
||||
last(): SessionBoundModel<M, InstanceProps> | undefined;
|
||||
|
||||
/**
|
||||
* Returns a new {@link QuerySet} instance with entities that match properties in `lookupObj`.
|
||||
*
|
||||
* @param lookupObj - the properties to match objects with ({@link LookupProps}).
|
||||
* Can also be a function ({@link LookupPredicate}).
|
||||
*
|
||||
* @return a new {@link QuerySet} instance with objects that passed the filter.
|
||||
*/
|
||||
filter<TLookup extends LookupSpec<M>>(lookupObj: TLookup): LookupResult<M, TLookup>;
|
||||
|
||||
/**
|
||||
* Returns a new {@link QuerySet} instance with entities that do not match properties in `lookupObj`.
|
||||
*
|
||||
* @param lookupObj - the properties to match objects with ({@link LookupProps}).
|
||||
* Can also be a function ({@link LookupPredicate}).
|
||||
*
|
||||
* @return a new {@link QuerySet} instance with objects that passed the filter.
|
||||
*/
|
||||
exclude<TLookup extends LookupSpec<M>>(lookupObj: TLookup): LookupResult<M, TLookup>;
|
||||
|
||||
/**
|
||||
* Returns a new {@link QuerySet} instance with entities ordered by `iteratees` in ascending
|
||||
* order, unless otherwise specified. Delegates to `lodash.orderBy`.
|
||||
*
|
||||
* @param iteratees - an array or a single {@link SortIteratee} where each item can be a string or a
|
||||
* function. If a string is supplied, it should
|
||||
* correspond to property on the entity that will
|
||||
* determine the order. If a function is supplied,
|
||||
* it should return the value to order by.
|
||||
*
|
||||
* @param [orders] - the sort orders of `iteratees`. If unspecified, all iteratees
|
||||
* will be sorted in ascending order. `true` and `'asc'`
|
||||
* correspond to ascending order, and `false` and `'desc`
|
||||
* to descending order. Accepts an array or a single {@link SortOrder}.
|
||||
*
|
||||
* @return a new {@link QuerySet} with objects ordered by `iteratees`.
|
||||
*/
|
||||
orderBy(
|
||||
iteratees: SortIteratee<M> | ReadonlyArray<SortIteratee<M>>,
|
||||
orders?: SortOrder | ReadonlyArray<SortOrder>
|
||||
): QuerySet<M>;
|
||||
|
||||
/**
|
||||
* Returns the number of {@link Model} instances represented by the QuerySet.
|
||||
*
|
||||
* @return length of the QuerySet
|
||||
*/
|
||||
count(): number;
|
||||
|
||||
/**
|
||||
* Checks if the {@link QuerySet} instance has any records matching the query
|
||||
* in the database.
|
||||
*
|
||||
* @return `true` if the {@link QuerySet} instance contains entities, else `false`.
|
||||
*/
|
||||
exists(): boolean;
|
||||
|
||||
/**
|
||||
* Returns an array of the plain objects represented by the QuerySet.
|
||||
* The plain objects are direct references to the store.
|
||||
*
|
||||
* @return references to the plain JS objects represented by the QuerySet
|
||||
*/
|
||||
toRefArray(): ReadonlyArray<Ref<M>>;
|
||||
|
||||
/**
|
||||
* Returns an array of {@link SessionBoundModel} instances represented by the QuerySet.
|
||||
*
|
||||
* @return session bound model instances represented by the QuerySet
|
||||
*/
|
||||
toModelArray(): ReadonlyArray<SessionBoundModel<M, InstanceProps>>;
|
||||
|
||||
/**
|
||||
* Records an update specified with `mergeObj` to all the objects
|
||||
* in the {@link QuerySet} instance.
|
||||
*
|
||||
* @param mergeObj - an object extending {@link UpdateProps}, to be merged with all the objects in this QuerySet.
|
||||
*/
|
||||
update(mergeObj: UpdateProps<M>): void;
|
||||
|
||||
/**
|
||||
* Records a deletion of all the objects in this {@link QuerySet} instance.
|
||||
*/
|
||||
delete(): void;
|
||||
|
||||
/**
|
||||
* Returns a string representation of QuerySet instance contents.
|
||||
*
|
||||
* @return string representation of QuerySet.
|
||||
*/
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link QuerySet} extensions available on {@link ManyToMany} fields of session bound {@link Model} instances.
|
||||
*/
|
||||
export interface ManyToManyExtensions<M extends AnyModel> {
|
||||
add: (...entitiesToAdd: ReadonlyArray<IdOrModelLike<M>>) => void;
|
||||
remove: (...entitiesToRemove: ReadonlyArray<IdOrModelLike<M>>) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link QuerySet} extended with {@link ManyToMany} specific functionality - {@link ManyToManyExtensions}.
|
||||
*/
|
||||
export interface MutableQuerySet<M extends AnyModel = any, InstanceProps extends object = {}>
|
||||
extends ManyToManyExtensions<M>,
|
||||
QuerySet<M, InstanceProps> {}
|
||||
72
types/redux-orm/Session.d.ts
vendored
Normal file
72
types/redux-orm/Session.d.ts
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
import { IndexedModelClasses, ORM, OrmState } from './ORM';
|
||||
import { Database, QueryResult, QuerySpec, UpdateSpec } from './db';
|
||||
import { Assign } from './helpers';
|
||||
import { ModelType } from './Model';
|
||||
|
||||
export type BatchToken = any;
|
||||
|
||||
export default class Session<I extends IndexedModelClasses<any>> {
|
||||
/**
|
||||
* list of bound {@link Model} classes bound to this session, bootstrapped during {@link @ORM.register}.
|
||||
*
|
||||
* @see {@link ModelType}
|
||||
*/
|
||||
readonly sessionBoundModels: ReadonlyArray<ModelType<InstanceType<I[keyof I]>>>;
|
||||
|
||||
/**
|
||||
* Current {@link OrmState}, specific to registered schema
|
||||
*/
|
||||
readonly state: OrmState<I>;
|
||||
|
||||
/**
|
||||
* 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 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<I>, db: Database<I>, state: OrmState<I>, withMutations?: boolean, batchToken?: BatchToken);
|
||||
|
||||
/**
|
||||
* Executes query against model state.
|
||||
*
|
||||
* @param query - the query command object.
|
||||
*
|
||||
* @returns query result.
|
||||
*
|
||||
* @see {@link QueryType}
|
||||
* @see {@link QueryClause}
|
||||
* @see {@link QuerySpec}
|
||||
* @see {@link QueryResult}
|
||||
*/
|
||||
query(query: QuerySpec): QueryResult;
|
||||
|
||||
/**
|
||||
* Applies update to a model state.
|
||||
*
|
||||
* @param update - the update command object.
|
||||
*
|
||||
* @returns query result.
|
||||
*
|
||||
* @see {@link DbAction}
|
||||
* @see {@link UpdateSpec}
|
||||
* @see {@link DbActionResult}
|
||||
* @see {@link UpdateResult}
|
||||
*/
|
||||
applyUpdate<P>(update: UpdateSpec<P>): P;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link ORM}-bound {@link Session} instance, extended with a set of {@link ModelType} properties.
|
||||
*
|
||||
* Extension is a map of {@link ModelType} accessible under keys within a set of {@link Model#modelName} values
|
||||
* for registered {@link Model} classes.
|
||||
*/
|
||||
export type OrmSession<I extends IndexedModelClasses<any>> = Assign<
|
||||
Session<I>,
|
||||
{ [K in keyof I]: ModelType<InstanceType<I[K]>> }
|
||||
>;
|
||||
10
types/redux-orm/constants.d.ts
vendored
Normal file
10
types/redux-orm/constants.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
export const UPDATE = 'REDUX_ORM_UPDATE';
|
||||
export const DELETE = 'REDUX_ORM_DELETE';
|
||||
export const CREATE = 'REDUX_ORM_CREATE';
|
||||
|
||||
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';
|
||||
160
types/redux-orm/db/Database.d.ts
vendored
Normal file
160
types/redux-orm/db/Database.d.ts
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
import { IndexedModelClasses, OrmState } from '../ORM';
|
||||
import { CREATE, DELETE, EXCLUDE, FAILURE, FILTER, ORDER_BY, SUCCESS, UPDATE } from '../constants';
|
||||
import { ModelTableOpts, Table } from './Table';
|
||||
import { SerializableMap } from '../Model';
|
||||
import { BatchToken } from '../Session';
|
||||
|
||||
/**
|
||||
* A type of {@link QueryClause}.
|
||||
*/
|
||||
export type QueryType = typeof FILTER | typeof EXCLUDE | typeof ORDER_BY;
|
||||
|
||||
/**
|
||||
* A single `QueryClause`.
|
||||
* Multiple `QueryClause`s can be combined into a {@link Query}.
|
||||
*/
|
||||
export interface QueryClause<Payload extends object = {}> {
|
||||
type: QueryType;
|
||||
payload: Payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query definition, contains target table and a collection of {@link QueryClause}.
|
||||
*/
|
||||
export interface Query {
|
||||
table: string;
|
||||
clauses: QueryClause[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Query wrapper definition, wraps {@link Query}.
|
||||
*/
|
||||
export interface QuerySpec {
|
||||
query: Query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query result.
|
||||
*/
|
||||
export interface QueryResult<Row extends SerializableMap = {}> {
|
||||
rows: ReadonlyArray<Row>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type of data update to perform.
|
||||
*/
|
||||
export type UpdateType = typeof CREATE | typeof UPDATE | typeof DELETE;
|
||||
|
||||
/**
|
||||
* A status of data update operation.
|
||||
*/
|
||||
export type UpdateStatus = typeof SUCCESS | typeof FAILURE;
|
||||
|
||||
/**
|
||||
* Data update definition
|
||||
*/
|
||||
export interface UpdateSpec<Payload = any> {
|
||||
action: UpdateType;
|
||||
payload?: Payload;
|
||||
query?: Query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data update result.
|
||||
*/
|
||||
export interface UpdateResult<I extends IndexedModelClasses<any>, Payload extends object = {}> {
|
||||
status: UpdateStatus;
|
||||
state: OrmState<I>;
|
||||
payload: Payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transactions aggregate batches of operations.
|
||||
*/
|
||||
export interface Transaction {
|
||||
batchToken: BatchToken;
|
||||
withMutations: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema specification, required for default database creator.
|
||||
*
|
||||
* @see {@link DatabaseCreator}
|
||||
* @see {@link ModelTableOpts}
|
||||
* @see {@link Table}
|
||||
*/
|
||||
export interface SchemaSpec<I extends IndexedModelClasses<any>> {
|
||||
tables: { [K in keyof I]: ModelTableOpts<I[K]> };
|
||||
}
|
||||
|
||||
/**
|
||||
* A Database parametrized by schema made of {@link Model} classes.
|
||||
*
|
||||
* @see {@link SchemaSpec}
|
||||
* @see {@link TableSpec}
|
||||
* @see {@link Table}
|
||||
*/
|
||||
export interface Database<I extends IndexedModelClasses<any>, Tables = { [K in keyof I]: Table<I[K]> }> {
|
||||
/**
|
||||
* Returns the empty database state.
|
||||
*
|
||||
* @see {@link OrmState}
|
||||
*
|
||||
* @return empty state
|
||||
*/
|
||||
getEmptyState(): OrmState<I>;
|
||||
|
||||
/**
|
||||
* Execute a query against a given state.
|
||||
*
|
||||
* @param querySpec - a query definition.
|
||||
* @param state - the state to query against.
|
||||
*
|
||||
* @see {@link QuerySpec}
|
||||
* @see {@link OrmState}
|
||||
* @see {@link OrmState}
|
||||
*
|
||||
* @return a {@link QueryResult} containing 0 to many {@link QueryResult.rows}.
|
||||
*/
|
||||
query(querySpec: QuerySpec, state: OrmState<I>): QueryResult;
|
||||
|
||||
/**
|
||||
* Apply an update to a given state.
|
||||
*
|
||||
* @param updateSpec - a data update definition.
|
||||
* @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}
|
||||
*
|
||||
* @return a {@link UpdateResult} containing 0 to many {@link QueryResult.rows}.
|
||||
*/
|
||||
|
||||
update(updateSpec: UpdateSpec, tx: Transaction, state: OrmState<I>): UpdateResult<I>;
|
||||
|
||||
/**
|
||||
* Return a {@link Table} structure based on provided table name.
|
||||
* @param tableName - the name of the {@link Table} to describe
|
||||
*
|
||||
* @return a {@link Table} instance matching given `tableName` or `undefined` if no such table exists.
|
||||
*/
|
||||
describe<K extends keyof Tables>(tableName: K): Tables[K];
|
||||
}
|
||||
|
||||
/**
|
||||
* Database creation function type.
|
||||
*/
|
||||
export type DatabaseCreator = typeof createDatabase;
|
||||
|
||||
/**
|
||||
* Default database creation procedure handle.
|
||||
*
|
||||
* @param schemaSpec - a {@link SchemaSpec} to built the {@link Database} from.
|
||||
*
|
||||
* @return a {@Link Database} instance, ready for query and data update operation.
|
||||
*/
|
||||
export function createDatabase<I extends IndexedModelClasses<any>>(schemaSpec: SchemaSpec<I>): Database<I>;
|
||||
|
||||
export default createDatabase;
|
||||
126
types/redux-orm/db/Table.d.ts
vendored
Normal file
126
types/redux-orm/db/Table.d.ts
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
import Model, { AnyModel, FieldSpecKeys, IdType, Ref } from '../Model';
|
||||
import { ForeignKey, OneToOne, TableOpts } from '../index';
|
||||
import { Field } from '../fields';
|
||||
|
||||
/**
|
||||
* {@link TableOpts} used for {@link Table} customization.
|
||||
*
|
||||
* Supplied via {@link Model#options}.
|
||||
*
|
||||
* If no customizations were provided, the table uses following default options:
|
||||
* <br/>
|
||||
* ```typescript
|
||||
* {
|
||||
* idAttribute: 'id',
|
||||
* arrName: 'items',
|
||||
* mapName: 'itemsById'
|
||||
* }
|
||||
* ```
|
||||
* <br/>
|
||||
* @see {@link Model}
|
||||
* @see {@link Model#options}
|
||||
* @see {@link OrmState}
|
||||
*/
|
||||
export interface TableOpts {
|
||||
readonly idAttribute?: string;
|
||||
readonly arrName?: string;
|
||||
readonly mapName?: string;
|
||||
readonly fields?: { [K: string]: Field };
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export 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}.
|
||||
*/
|
||||
export type IdAttribute<MClass extends typeof AnyModel> = ExtractModelOption<MClass, 'idAttribute', 'id'>;
|
||||
|
||||
/**
|
||||
* Model arrName option extraction helper.
|
||||
*
|
||||
* Falls back to `'items'` if not specified explicitly via {@link Model.options}.
|
||||
*/
|
||||
export type ArrName<MClass extends typeof AnyModel> = ExtractModelOption<MClass, 'arrName', 'items'>;
|
||||
|
||||
/**
|
||||
* Model mapName option extraction helper.
|
||||
*
|
||||
* Falls back to `'itemsById'` if not specified explicitly via {@link Model.options}.
|
||||
*/
|
||||
export type MapName<MClass extends typeof AnyModel> = ExtractModelOption<MClass, 'mapName', 'itemsById'>;
|
||||
|
||||
/**
|
||||
* Unbox {@link Model#options} or fallback to default for others.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface ModelTableOpts<MClass extends typeof AnyModel> {
|
||||
readonly idAttribute: IdAttribute<MClass>;
|
||||
readonly arrName: ArrName<MClass>;
|
||||
readonly mapName: MapName<MClass>;
|
||||
readonly fields: MClass['fields'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the underlying data structure for a {@link Model} class.
|
||||
*/
|
||||
export class Table<MClass extends typeof AnyModel> {
|
||||
/**
|
||||
* Creates a new {@link Table} instance.
|
||||
*
|
||||
* @param userOpts - options to use.
|
||||
* @param [userOpts.idAttribute=DefaultTableOpts.idAttribute] - the id attribute of the entity.
|
||||
* @param [userOpts.arrName=DefaultTableOpts.arrName] - the state attribute where an array of
|
||||
* entity id's are stored
|
||||
* @param [userOpts.mapName=DefaultTableOpts.mapName] - the state attribute where the entity objects
|
||||
* are stored in a id to entity object
|
||||
* map.
|
||||
* @param [userOpts.fields=DefaultTableOpts.fields] - mapping of field key to {@link Field} object
|
||||
*/
|
||||
constructor(userOpts?: ModelTableOpts<MClass>);
|
||||
|
||||
getEmptyState(): TableState<MClass>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of {@link Model} state's branch `meta` field.
|
||||
*/
|
||||
export interface DefaultMeta<MIdType> {
|
||||
maxId: MIdType extends number ? number : null | number;
|
||||
}
|
||||
|
||||
export type TableIndexes<MClass extends typeof AnyModel> = {
|
||||
[K in FieldSpecKeys<InstanceType<MClass>, OneToOne | ForeignKey>]: string
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapped type parametrized by specific {@link Model} class.
|
||||
*
|
||||
* Infers actual state of the ORM branch based on the {@link Model} class provided.
|
||||
*/
|
||||
export type TableState<MClass extends typeof AnyModel> = {
|
||||
readonly meta: DefaultMeta<IdType<InstanceType<MClass>>>;
|
||||
readonly indexes: TableIndexes<MClass>;
|
||||
} & { readonly [K in ArrName<MClass>]: ReadonlyArray<IdType<InstanceType<MClass>>> } &
|
||||
{
|
||||
readonly [K in MapName<MClass>]: {
|
||||
readonly [K: string]: Ref<InstanceType<MClass>>;
|
||||
}
|
||||
};
|
||||
5
types/redux-orm/db/index.d.ts
vendored
Normal file
5
types/redux-orm/db/index.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
import createDatabase = require('./Database');
|
||||
|
||||
export * from './Database';
|
||||
export { ModelTableOpts, TableOpts, TableState, Table } from './Table';
|
||||
export default createDatabase;
|
||||
73
types/redux-orm/fields.d.ts
vendored
Normal file
73
types/redux-orm/fields.d.ts
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
export class Field {
|
||||
readonly index: boolean;
|
||||
}
|
||||
|
||||
export interface AttributeOpts {
|
||||
getDefault?: () => any;
|
||||
}
|
||||
|
||||
export class Attribute extends Field {
|
||||
constructor(opts?: AttributeOpts);
|
||||
}
|
||||
|
||||
export interface AttributeWithDefault extends Attribute {
|
||||
getDefault(): any;
|
||||
}
|
||||
|
||||
export interface RelationalFieldOpts {
|
||||
to: string;
|
||||
relatedName?: string;
|
||||
through?: string;
|
||||
throughFields?: {
|
||||
to: string;
|
||||
from: string;
|
||||
};
|
||||
as?: string;
|
||||
}
|
||||
|
||||
export class RelationalField extends Field {
|
||||
constructor(toModelName: string, relatedName?: string);
|
||||
constructor(opts: RelationalFieldOpts);
|
||||
}
|
||||
|
||||
export class OneToOne extends RelationalField {}
|
||||
|
||||
export class ForeignKey extends RelationalField {
|
||||
readonly index: true;
|
||||
}
|
||||
|
||||
export class ManyToMany extends RelationalField {
|
||||
readonly index: false;
|
||||
}
|
||||
|
||||
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 interface OneToOneCreator {
|
||||
(toModelName: string, relatedName?: string): OneToOne;
|
||||
(opts: RelationalFieldOpts): OneToOne;
|
||||
}
|
||||
|
||||
export const attr: AttrCreator;
|
||||
|
||||
export const oneToOne: OneToOneCreator;
|
||||
|
||||
export const fk: FkCreator;
|
||||
|
||||
export const many: ManyCreator;
|
||||
|
||||
export interface FieldSpecMap {
|
||||
[K: string]: Attribute | ForeignKey | ManyToMany | OneToOne;
|
||||
}
|
||||
23
types/redux-orm/helpers.d.ts
vendored
Normal file
23
types/redux-orm/helpers.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Credits to Piotr Witek (http://piotrwitek.github.io) and utility-type project (https://github.com/piotrwitek/utility-types)
|
||||
*/
|
||||
|
||||
export type Assign<T extends object, U extends object, I = Diff<T, U> & Intersection<U, T> & Diff<U, T>> = Pick<
|
||||
I,
|
||||
keyof I
|
||||
>;
|
||||
|
||||
export type Diff<T extends object, U extends object> = Pick<T, Exclude<keyof T, keyof U>>;
|
||||
|
||||
export type PickByValue<T, ValueType> = Pick<T, { [Key in keyof T]: T[Key] extends ValueType ? Key : never }[keyof T]>;
|
||||
|
||||
export type Overwrite<T extends object, U extends object, I = Diff<T, U> & Intersection<U, T>> = Pick<I, keyof I>;
|
||||
|
||||
export type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
export type Intersection<T extends object, U extends object> = Pick<
|
||||
T,
|
||||
Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
|
||||
>;
|
||||
|
||||
export type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
|
||||
287
types/redux-orm/index.d.ts
vendored
287
types/redux-orm/index.d.ts
vendored
@ -1,215 +1,84 @@
|
||||
// Type definitions for redux-orm 0.9
|
||||
// Type definitions for redux-orm 0.13
|
||||
// Project: https://github.com/redux-orm/redux-orm
|
||||
// Definitions by: Andrey Goncharov <https://github.com/keenondrums>
|
||||
// Tomasz Zabłocki <https://github.com/tomasz-zablocki>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.3
|
||||
// TypeScript Version: 3.5
|
||||
|
||||
export interface ORMId {
|
||||
id: string;
|
||||
}
|
||||
import { ORM, ORMOpts, OrmState } from './ORM';
|
||||
import Model, {
|
||||
CreateProps,
|
||||
CustomInstanceProps,
|
||||
IdKey,
|
||||
IdOrModelLike,
|
||||
IdType,
|
||||
ModelField,
|
||||
ModelFieldMap,
|
||||
ModelType,
|
||||
Ref,
|
||||
RefPropOrSimple,
|
||||
SessionBoundModel,
|
||||
UpdateProps,
|
||||
UpsertProps
|
||||
} from './Model';
|
||||
import QuerySet, {
|
||||
LookupPredicate,
|
||||
LookupProps,
|
||||
LookupResult,
|
||||
LookupSpec,
|
||||
MutableQuerySet,
|
||||
SortIteratee,
|
||||
SortOrder
|
||||
} from './QuerySet';
|
||||
import { OrmSession } from './Session';
|
||||
import { createDatabase, TableOpts, TableState } from './db';
|
||||
import { attr, Attribute, FieldSpecMap, fk, ForeignKey, many, ManyToMany, OneToOne, oneToOne } from './fields';
|
||||
import { createReducer, createSelector, defaultUpdater, ORMReducer, ORMSelector } from './redux';
|
||||
|
||||
export interface TableState<Item = any, Meta = any> {
|
||||
items: string[];
|
||||
itemsById: { [index: string]: Item };
|
||||
meta: Meta;
|
||||
}
|
||||
export {
|
||||
FieldSpecMap,
|
||||
LookupResult,
|
||||
LookupSpec,
|
||||
LookupPredicate,
|
||||
LookupProps,
|
||||
TableOpts,
|
||||
RefPropOrSimple,
|
||||
ModelFieldMap,
|
||||
CustomInstanceProps,
|
||||
UpsertProps,
|
||||
CreateProps,
|
||||
UpdateProps,
|
||||
ModelField,
|
||||
SortIteratee,
|
||||
OrmSession as Session,
|
||||
SortOrder,
|
||||
MutableQuerySet,
|
||||
createDatabase,
|
||||
createSelector,
|
||||
createReducer,
|
||||
defaultUpdater,
|
||||
ORMSelector,
|
||||
ORMReducer,
|
||||
IdOrModelLike,
|
||||
Ref,
|
||||
SessionBoundModel,
|
||||
IdKey,
|
||||
IdType,
|
||||
ModelType,
|
||||
ORM,
|
||||
OrmState,
|
||||
ORMOpts,
|
||||
TableState,
|
||||
Model,
|
||||
QuerySet,
|
||||
Attribute,
|
||||
OneToOne,
|
||||
ForeignKey,
|
||||
ManyToMany,
|
||||
attr,
|
||||
oneToOne,
|
||||
fk,
|
||||
many
|
||||
};
|
||||
|
||||
export interface ORMCommonState {
|
||||
[index: string]: TableState;
|
||||
}
|
||||
|
||||
export type SessionWithModels<State extends ORMCommonState> = Session<State> & { [P in keyof State]: typeof Model };
|
||||
|
||||
export type ModelWithFields<Fields, Additional = {}, VirtualFields = {}> = Model<Fields, Additional, VirtualFields> & Fields & VirtualFields & Additional & ORMId;
|
||||
|
||||
// TODO: Refine me
|
||||
export type ModelProps = any;
|
||||
|
||||
// TODO: Refine me
|
||||
export interface DB {
|
||||
getEmptyState: any;
|
||||
query: any;
|
||||
update: any;
|
||||
describe: any;
|
||||
}
|
||||
|
||||
// TODO: Refine me
|
||||
export interface SchemaSpec {
|
||||
tables: any;
|
||||
}
|
||||
|
||||
// TODO: Refine me
|
||||
export interface ORMOpts {
|
||||
createDatabase: (schemaSpec: SchemaSpec) => any;
|
||||
}
|
||||
|
||||
export interface ModelFields {
|
||||
[index: string]: Attribute | ForeignKey | ManyToMany | OneToOne;
|
||||
}
|
||||
|
||||
export interface ModelVirtualFields {
|
||||
[index: string]: any;
|
||||
}
|
||||
|
||||
export class ORM<State extends ORMCommonState = ORMCommonState> {
|
||||
constructor(opts?: ORMOpts)
|
||||
register(...model: Array<typeof Model>): void;
|
||||
register<M>(...model: Array<M[keyof M]>): void;
|
||||
registerManyToManyModelsFor(model: typeof Model): void;
|
||||
get(modelName: string): typeof Model;
|
||||
getModelClasses(): Array<typeof Model>;
|
||||
isFieldInstalled(modelName: string, fieldName: string): boolean;
|
||||
setFieldInstalled(modelName: string, fieldName: string): void;
|
||||
generateSchemaSpec(): SchemaSpec;
|
||||
getDatabase(): DB;
|
||||
getEmptyState(): State;
|
||||
session(state: State): SessionWithModels<State>;
|
||||
mutableSession(state: State): SessionWithModels<State>;
|
||||
|
||||
private _attachQuerySetMethods(model: typeof Model): void;
|
||||
private _setupModelPrototypes(models: Array<typeof Model>): void;
|
||||
}
|
||||
|
||||
export class Model<Fields, Additional = {}, VirtualFields = {}> {
|
||||
static readonly idAttribute: string;
|
||||
static readonly session: SessionWithModels<any>;
|
||||
static readonly _sessionData: any; // TODO
|
||||
static readonly query: QuerySet<any>;
|
||||
|
||||
static modelName: string;
|
||||
static fields: ModelFields;
|
||||
static virtualFields: ModelVirtualFields;
|
||||
static querySetClass: typeof QuerySet;
|
||||
|
||||
static toString(): string;
|
||||
static options(): object;
|
||||
static _getTableOpts(): object;
|
||||
static markAccessed(): void;
|
||||
static connec(session: Session<ORMCommonState>): void;
|
||||
static getQuerySet(): QuerySet<any>;
|
||||
static invalidateClassCache(): void;
|
||||
static all(): QuerySet<any, any, any>;
|
||||
static create<Fields>(props: Fields): ModelWithFields<Fields, any, any>;
|
||||
static upsert<Fields>(props: Partial<Fields>): ModelWithFields<Fields, any, any>;
|
||||
static withId(id: string): ModelWithFields<any, any, any>;
|
||||
static hasId(id: string): boolean;
|
||||
static _findDatabaseRows(lookupObj: object): any; // TODO
|
||||
static get(lookupObj: object): ModelWithFields<any, any, any>;
|
||||
static reducer(action: any, modelClass: typeof Model, session: SessionWithModels<ORMCommonState>): any;
|
||||
|
||||
readonly ref: Fields & Additional & ORMId;
|
||||
|
||||
constructor(props: ModelProps)
|
||||
|
||||
getClass(): string;
|
||||
getId(): string;
|
||||
toString(): string;
|
||||
equals(otherModel: ModelWithFields<any, any, any>): boolean;
|
||||
set(propertyName: string, value: any): void;
|
||||
update(userMergeObj: Partial<Fields & Additional>): void;
|
||||
refreshFromState(): void;
|
||||
delete(): void;
|
||||
|
||||
private _onDelete(): void;
|
||||
private _initFields(props: ModelProps): void;
|
||||
private _refreshMany2Many(relations: any): void; // TODO
|
||||
}
|
||||
|
||||
export type QuerySetClauses = any; // TODO
|
||||
export type QuerySetOpts = any; // TODO
|
||||
export class QuerySet<Fields, Additional = {}, VirtualFields = {}> {
|
||||
static addSharedMethod(methodName: string): void;
|
||||
|
||||
constructor(modelClass: typeof Model, clauses: QuerySetClauses, opts: QuerySetOpts)
|
||||
|
||||
toString(): string;
|
||||
toRefArray(): Array<Fields & Additional & ORMId>;
|
||||
toModelArray(): Array<ModelWithFields<Fields, Additional, VirtualFields>>;
|
||||
count(): number;
|
||||
exists(): boolean;
|
||||
at(index: string): ModelWithFields<Fields, Additional, VirtualFields> | undefined;
|
||||
first(): ModelWithFields<Fields, Additional, VirtualFields> | undefined;
|
||||
last(): ModelWithFields<Fields, Additional, VirtualFields> | undefined;
|
||||
all(): QuerySet<Fields, Additional, VirtualFields>;
|
||||
filter(lookupObj: object): QuerySet<Fields, Additional, VirtualFields>; // TODO
|
||||
exclude(lookupObj: object): QuerySet<Fields, Additional, VirtualFields>; // TODO
|
||||
orderBy(iteratees: any, orders: any): QuerySet<Fields, Additional, VirtualFields>; // TODO
|
||||
update(mergeObj: Partial<Fields & Additional>): void;
|
||||
delete(): void;
|
||||
|
||||
private _evaluate(): void;
|
||||
private _new(clauses: QuerySetClauses, userOpts: QuerySetOpts): QuerySet<Fields, Additional, VirtualFields>;
|
||||
}
|
||||
|
||||
export class Session<State extends ORMCommonState> {
|
||||
readonly accessedModels: string[];
|
||||
schema: ORM<State>;
|
||||
db: DB;
|
||||
initialState: State;
|
||||
withMutations: boolean;
|
||||
batchToken: any;
|
||||
sessionBoundModels: Array<typeof Model>;
|
||||
models: Array<typeof Model>;
|
||||
state: State;
|
||||
|
||||
constructor(schema: ORM<State>, db: DB, state: State, withMutations: boolean, batchToken: any) // TODO
|
||||
|
||||
markAccessed(modelName: string): void;
|
||||
getDataForModel(modelName: string): object;
|
||||
applyUpdate(updateSpec: any): any; // TODO
|
||||
query(querySpec: any): any; // TODO
|
||||
}
|
||||
|
||||
export interface AttributeOpts {
|
||||
getDefault?: () => any;
|
||||
}
|
||||
|
||||
export class Attribute {
|
||||
constructor(opts: AttributeOpts)
|
||||
install(model: typeof Model, fieldName: string, orm: ORM): void;
|
||||
}
|
||||
|
||||
export interface RelationalFieldOpts {
|
||||
to: string;
|
||||
relatedName?: string;
|
||||
through?: string;
|
||||
throughFields?: {
|
||||
to: string;
|
||||
from: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class RelationalField {
|
||||
constructor(toModelName: string, relatedName?: string)
|
||||
constructor(opts: RelationalFieldOpts)
|
||||
getClass: typeof RelationalField;
|
||||
}
|
||||
|
||||
export class ForeignKey extends RelationalField {
|
||||
install(model: typeof Model, fieldName: string, orm: ORM): void;
|
||||
}
|
||||
|
||||
export class ManyToMany extends RelationalField {
|
||||
install(model: typeof Model, fieldName: string, orm: ORM): void;
|
||||
}
|
||||
|
||||
export class OneToOne extends RelationalField {
|
||||
install(model: typeof Model, fieldName: string, orm: ORM): void;
|
||||
}
|
||||
|
||||
export function attr(opts?: AttributeOpts): Attribute;
|
||||
|
||||
export function fk(toModelName: string, relatedName?: string): ForeignKey;
|
||||
export function fk(opts: RelationalFieldOpts): ForeignKey;
|
||||
|
||||
export function many(toModelName: string, relatedName?: string): ManyToMany;
|
||||
export function oneToOne(toModelName: string, relatedName?: string): OneToOne;
|
||||
export function oneToOne(opts: RelationalFieldOpts): OneToOne;
|
||||
|
||||
export type Updater<State extends ORMCommonState> = (session: SessionWithModels<State>, action: any) => any;
|
||||
|
||||
export function createReducer<State extends ORMCommonState = ORMCommonState>(orm: ORM<State>, updater?: Updater<State>): (state: State, action: any) => State;
|
||||
|
||||
export type ORMSelector<State extends ORMCommonState, Result = any> = (session: SessionWithModels<State>, ...args: any[]) => Result;
|
||||
|
||||
export function createSelector<State extends ORMCommonState = ORMCommonState>(orm: ORM<State>, ...args: Array<ORMSelector<State>>): (state: State) => any;
|
||||
export default Model;
|
||||
|
||||
@ -1,78 +1,374 @@
|
||||
import { attr, createSelector as createSelectorORM, ORMCommonState, ORMId, QuerySet, TableState, SessionWithModels, Model, ORM } from 'redux-orm';
|
||||
import {
|
||||
attr,
|
||||
createSelector as createOrmSelector,
|
||||
fk,
|
||||
IdKey,
|
||||
IdType,
|
||||
many,
|
||||
Model,
|
||||
ModelType,
|
||||
MutableQuerySet,
|
||||
ORM,
|
||||
OrmState,
|
||||
QuerySet,
|
||||
Ref
|
||||
} from 'redux-orm';
|
||||
|
||||
// Model
|
||||
export class Test extends Model<TestStateItem, FetchIndicatorState> {
|
||||
static modelName = 'Test';
|
||||
|
||||
static fields = {
|
||||
test: attr(),
|
||||
isFetching: attr({ getDefault: () => false }),
|
||||
id: attr()
|
||||
};
|
||||
interface CreateBookAction {
|
||||
type: 'CREATE_BOOK';
|
||||
payload: { coverArt?: string; title: string; publisher: number; authors?: string[] };
|
||||
}
|
||||
|
||||
// core data which we do not have defaults for
|
||||
export interface TestStateItem {
|
||||
test: string;
|
||||
interface DeleteBookAction {
|
||||
type: 'DELETE_BOOK';
|
||||
payload: { title: string };
|
||||
}
|
||||
|
||||
// optional data we provide defaults for
|
||||
export interface FetchIndicatorState {
|
||||
isFetching: boolean;
|
||||
type RootAction = CreateBookAction | DeleteBookAction;
|
||||
|
||||
interface BookFields {
|
||||
title: string;
|
||||
coverArt: string;
|
||||
publisher: Publisher;
|
||||
authors?: MutableQuerySet<Person>;
|
||||
}
|
||||
|
||||
// id attr is added automatically by redux-orm therefore we have IORMId interface
|
||||
export type TestState = TableState<TestStateItem & ORMId & FetchIndicatorState>;
|
||||
|
||||
export interface TestORMState extends ORMCommonState {
|
||||
Test: TestState;
|
||||
class Book extends Model<typeof Book, BookFields> {
|
||||
static modelName = 'Book' as const;
|
||||
static fields = {
|
||||
title: attr(),
|
||||
coverArt: attr({ getDefault: () => 'empty.png' }),
|
||||
publisher: fk('Publisher', 'books'),
|
||||
authors: many({ to: 'Person', relatedName: 'books', through: 'Authorship' })
|
||||
};
|
||||
static options = {
|
||||
idAttribute: 'title' as const
|
||||
};
|
||||
static reducer(action: RootAction, Book: ModelType<Book>) {
|
||||
switch (action.type) {
|
||||
case 'CREATE_BOOK':
|
||||
Book.create(action.payload);
|
||||
break;
|
||||
case 'DELETE_BOOK':
|
||||
Book.filter(book => book.title === action.payload.title).delete();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface TestORMModels {
|
||||
Test: typeof Test;
|
||||
interface PersonFields {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
nationality?: string;
|
||||
books?: MutableQuerySet<Book>;
|
||||
}
|
||||
|
||||
const orm = new ORM<TestORMState>();
|
||||
orm.register<TestORMModels>(Test);
|
||||
|
||||
// Reducer
|
||||
interface TestDTO {
|
||||
test: string;
|
||||
class Person extends Model<typeof Person, PersonFields> {
|
||||
static modelName = 'Person' as const;
|
||||
static fields = {
|
||||
id: attr(),
|
||||
firstName: attr(),
|
||||
lastName: attr(),
|
||||
nationality: attr()
|
||||
};
|
||||
}
|
||||
|
||||
interface Action<P> {
|
||||
type: string;
|
||||
payload: P;
|
||||
interface AuthorshipFields {
|
||||
year?: number;
|
||||
book: Book;
|
||||
author: Person;
|
||||
}
|
||||
|
||||
const reducerAddItem = (state: TestORMState, action: Action<TestDTO>): TestORMState => {
|
||||
const session = orm.session(state);
|
||||
session.Test.upsert<TestStateItem>(action.payload);
|
||||
return session.state;
|
||||
class Authorship extends Model<typeof Authorship, AuthorshipFields> {
|
||||
static modelName = 'Authorship' as const;
|
||||
static fields = {
|
||||
year: attr(),
|
||||
book: fk('Book'),
|
||||
author: fk('Person')
|
||||
};
|
||||
}
|
||||
|
||||
interface PublisherFields {
|
||||
index: number;
|
||||
name: string;
|
||||
books?: QuerySet<Book>;
|
||||
}
|
||||
|
||||
class Publisher extends Model<typeof Publisher, PublisherFields> {
|
||||
static modelName = 'Publisher' as const;
|
||||
static fields = {
|
||||
index: attr(),
|
||||
name: attr()
|
||||
};
|
||||
static options = {
|
||||
idAttribute: 'index' as const
|
||||
};
|
||||
}
|
||||
|
||||
const schema = { Book, Authorship, Person, Publisher };
|
||||
|
||||
type Schema = typeof schema;
|
||||
|
||||
// create ORM instance and register { Book, Publisher, Person, Authorship } schema
|
||||
const ormFixture = () => {
|
||||
const orm = new ORM<Schema>();
|
||||
orm.register(Book, Authorship, Person, Publisher);
|
||||
return orm;
|
||||
};
|
||||
|
||||
// Selector
|
||||
interface TestDisplayItem {
|
||||
test: string;
|
||||
}
|
||||
type TestDisplayItemList = TestDisplayItem[];
|
||||
|
||||
// Just for the example below. Use real createSelector from reselect in your app
|
||||
const createSelector = <S, P1, R>(param1Creator: (state: S) => P1, combiner: (param1: P1) => R): (state: S) => R => (state) =>
|
||||
combiner(param1Creator(state));
|
||||
|
||||
interface RootState {
|
||||
test: TestORMState;
|
||||
}
|
||||
|
||||
export const makeGetTestDisplayList = () => {
|
||||
const ormSelector = createSelectorORM<TestORMState>(orm, (session: SessionWithModels<TestORMState>) =>
|
||||
(session.Test.all() as QuerySet<TestStateItem, FetchIndicatorState>)
|
||||
.toRefArray()
|
||||
.map((item) => ({ ...item }))
|
||||
);
|
||||
return createSelector<RootState, TestORMState, TestDisplayItemList>(
|
||||
({ test }) => test,
|
||||
ormSelector
|
||||
);
|
||||
// create ORM instance and acquire new session
|
||||
const sessionFixture = () => {
|
||||
const orm = ormFixture();
|
||||
return orm.session(orm.getEmptyState());
|
||||
};
|
||||
|
||||
// inferred optionality of ModelType.create argument properties
|
||||
const argOptionalityAtModelCreation = () => {
|
||||
const { Book, Publisher, Person } = sessionFixture();
|
||||
|
||||
/**
|
||||
* 1.A. `number` Model identifiers are optional due to built-in incremental sequencing of numeric identifiers
|
||||
* @see {@link PublisherFields.index}
|
||||
*/
|
||||
const publisher = Publisher.create({ name: 'P1' });
|
||||
|
||||
/**
|
||||
* 1.B. `string` identifiers are mandatory
|
||||
*/
|
||||
const stringIdMissing = Book.create({ publisher: 1, coverArt: 'foo.bmp' }); // $ExpectError
|
||||
|
||||
/**
|
||||
* 2. non-relational fields with corresponding descriptors that contain defined `getDefault` callback: (`attr({ getDefault: () => 'empty.png' })`)
|
||||
* @see {@link Book#fields.coverArt}
|
||||
*/
|
||||
const book2 = Book.create({ title: 'B2', publisher: 1 });
|
||||
|
||||
/**
|
||||
* 3. both attribute and relational fields where corresponding ModelFields interface property has optional (`?`) modifier
|
||||
* @see {@link BookFields.authors}
|
||||
*/
|
||||
const book1 = Book.create({ title: 'B1', publisher: 1, coverArt: 'foo.bmp' });
|
||||
};
|
||||
|
||||
// ModelFields contribute to type constraints within ModelType.create arguments
|
||||
const argPropertyTypeRestrictionsOnCreate = () => {
|
||||
const { Book, Publisher, Person } = sessionFixture();
|
||||
|
||||
/** Keys of declared model fields interface contribute strict requirements regarding corresponding property types */
|
||||
Book.create({ title: 'B1', publisher: 1, coverArt: 'foo.png', authors: ['A1'] });
|
||||
|
||||
/* Incompatible property types: */
|
||||
Book.create({ title: 1, publisher: 1 }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: 'P1' }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: 1, coverArt: 4 }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: 1, authors: {} }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: 1, authors: () => null }); // $ExpectError
|
||||
|
||||
/**
|
||||
* Properties associated to relational fields may be supplied with:
|
||||
*
|
||||
* - a primitive type matching id type of relation target
|
||||
* - a Ref type derived from relation target
|
||||
* - Model/SessionBoundModel instance matching relation target
|
||||
* - a map containing {Idkey:IdType} entry, where IdKey/IdType are compatible with relation target id key:type signature
|
||||
*
|
||||
* In case of MutableQuerySets/many-to-many relationships, an array of union of above-mentioned types is accepted
|
||||
*/
|
||||
const authorModel = Person.create({ id: 'A1', firstName: 'A1', lastName: 'A1' });
|
||||
const publisherModel = Publisher.create({ name: 'P1' });
|
||||
Book.create({ title: 'B1', publisher: publisherModel, authors: [authorModel] });
|
||||
Book.create({ title: 'B1', publisher: publisherModel.ref, authors: [authorModel.ref] });
|
||||
Book.create({
|
||||
title: 'B1',
|
||||
publisher: { index: publisherModel.index },
|
||||
authors: [{ id: authorModel.id }, 'A1', authorModel, authorModel.ref]
|
||||
});
|
||||
|
||||
/** Id types are verified to match relation target */
|
||||
Book.create({ title: 'B1', publisher: authorModel }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: publisherModel.ref, authors: [publisherModel.ref, 'A1'] }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: { index: 'P1 ' } }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: { index: 0 }, authors: [authorModel, true] }); // $ExpectError
|
||||
};
|
||||
|
||||
// ModelFields contribute to type constraints within ModelType.create arguments
|
||||
const argPropertyTypeRestrictionsOnUpsert = () => {
|
||||
const { Book, Publisher, Person } = sessionFixture();
|
||||
|
||||
/** Upsert requires id to be provided */
|
||||
Book.upsert({ publisher: 1 }); // $ExpectError
|
||||
|
||||
// $ExpectType SessionBoundModel<Book, { title: string; publisher: number; }>
|
||||
Book.upsert({ title: 'B1', publisher: 1 });
|
||||
|
||||
/* Incompatible property types: */
|
||||
Book.upsert({ title: 4, publisher: 'P1' }); // $ExpectError
|
||||
Book.upsert({ title: 'B1', publisher: 'P1' }); // $ExpectError
|
||||
Book.upsert({ title: 'B1', publisher: 1, coverArt: 4 }); // $ExpectError
|
||||
Book.upsert({ title: 'B1', publisher: 1, authors: {} }); // $ExpectError
|
||||
Book.upsert({ title: 'B1', publisher: 1, authors: () => null }); // $ExpectError
|
||||
|
||||
/**
|
||||
* Properties associated to relational fields may be supplied with:
|
||||
*
|
||||
* - a primitive type matching id type of relation target
|
||||
* - a Ref type derived from relation target
|
||||
* - Model/SessionBoundModel instance matching relation target
|
||||
* - a map containing {Idkey:IdType} entry, where IdKey/IdType are compatible with relation target id key:type signature
|
||||
*
|
||||
* In case of MutableQuerySets/many-to-many relationships, an array of union of above-mentioned types is accepted
|
||||
*/
|
||||
const authorModel = Person.upsert({ id: 'A1', firstName: 'A1', lastName: 'A1' });
|
||||
const publisherModel = Publisher.upsert({ name: 'P1', index: 1 });
|
||||
Book.upsert({ title: 'B1', publisher: publisherModel, authors: [authorModel] });
|
||||
Book.upsert({ title: 'B1', publisher: publisherModel.ref, authors: [authorModel.ref] });
|
||||
Book.upsert({
|
||||
title: 'B1',
|
||||
publisher: { index: publisherModel.index },
|
||||
authors: [{ id: authorModel.id }, 'A1', authorModel, authorModel.ref]
|
||||
});
|
||||
|
||||
/** Id types are verified to match relation target */
|
||||
Book.create({ title: 'B1', publisher: authorModel }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: publisherModel.ref, authors: [publisherModel.ref, 'A1'] }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: { index: 'P1 ' } }); // $ExpectError
|
||||
Book.create({ title: 'B1', publisher: { index: 0 }, authors: [authorModel, true] }); // $ExpectError
|
||||
};
|
||||
|
||||
// restriction of allowed ORM.register args
|
||||
const restrictRegisterArgsToSchemaModels = () => {
|
||||
const incompleteSchema = { Book, Authorship, Person };
|
||||
const orm = new ORM<typeof incompleteSchema>();
|
||||
orm.register(Book, Authorship, Person, Publisher); // $ExpectError
|
||||
};
|
||||
|
||||
// inference of ORM branch state type
|
||||
const inferOrmBranchEmptyState = () => {
|
||||
const emptyState = ormFixture().getEmptyState();
|
||||
|
||||
const bookTableState = emptyState.Book; // $ExpectType TableState<typeof Book>
|
||||
const bookItemsById = emptyState.Book.itemsById; // $ExpectType { readonly [K: string]: Ref<Book>; }
|
||||
const authorshipMetaState = emptyState.Authorship.meta.maxId; // $ExpectType number
|
||||
const bookMetaState = emptyState.Book.meta.maxId; // $ExpectType number | null
|
||||
};
|
||||
|
||||
// indexing session instance using registered Model.modelName returns narrowed Model class
|
||||
const sessionInstanceExtendedWithNarrowedModelClasses = () => {
|
||||
const { Book, Person, Publisher } = sessionFixture();
|
||||
|
||||
// $ExpectType { Book: ModelType<Book>; Person: ModelType<Person>; Publisher: ModelType<Publisher>; }
|
||||
const sessionBoundModels = { Book, Person, Publisher };
|
||||
};
|
||||
|
||||
// IdKey and IdType mapped types support for valid identifier configurations
|
||||
const idInferenceAndCustomizations = () => {
|
||||
type ExtractId<M extends Model> = [IdKey<M>, IdType<M>];
|
||||
|
||||
type ImplicitDefault = ExtractId<Authorship>; // $ExpectType ["id", number]
|
||||
type CustomKey = ExtractId<Publisher>; // $ExpectType ["index", number]
|
||||
type CustomType = ExtractId<Person>; // $ExpectType ["id", string]
|
||||
type CustomKeyAndType = ExtractId<Book>; // $ExpectType ["title", string]
|
||||
};
|
||||
|
||||
// Model#create result retains custom properties supplied during call
|
||||
const customInstanceProperties = () => {
|
||||
const { Book } = sessionFixture();
|
||||
|
||||
const basicBook = Book.create({ title: 'book', publisher: 1 });
|
||||
|
||||
type basicBookKeys = Exclude<keyof typeof basicBook, keyof Model>; // $ExpectType "title" | "coverArt" | "publisher" | "authors"
|
||||
const basicBookTitle = basicBook.title; // $ExpectType string
|
||||
const authors = basicBook.authors; // $ExpectType MutableQuerySet<Person, {}> | undefined
|
||||
const unknownPropertyError = basicBook.customProp; // $ExpectError
|
||||
|
||||
const customProp = { foo: 0, bar: true };
|
||||
|
||||
const extendedBook = Book.create({
|
||||
title: 'extendedBook',
|
||||
publisher: 1,
|
||||
customProp
|
||||
});
|
||||
|
||||
type customBookKeys = Exclude<keyof typeof extendedBook, keyof Model>; // $ExpectType "title" | "coverArt" | "publisher" | "authors" | "customProp"
|
||||
const extendedBookTitle = extendedBook.title; // $ExpectType string
|
||||
const instanceCustomProp = extendedBook.customProp; // $ExpectType { foo: number; bar: boolean; }
|
||||
};
|
||||
|
||||
// reducer API is intact
|
||||
const standaloneReducerFunction = () => {
|
||||
const orm = ormFixture();
|
||||
|
||||
type StateType = OrmState<Schema>;
|
||||
|
||||
const reducerAddItem = (state: StateType, action: CreateBookAction): StateType => {
|
||||
const session = orm.session(state);
|
||||
session.Book.create(action.payload);
|
||||
return session.state;
|
||||
};
|
||||
};
|
||||
|
||||
// QuerySet type is retained though query chain until terminated.
|
||||
// Orders are optional, must conform to SortOrder type when present.
|
||||
// QuerySet.orderBy overloads accept iteratees applicable to QuerySet's type only
|
||||
const orderByArguments = () => {
|
||||
const { Book } = sessionFixture();
|
||||
const booksQuerySet = Book.all();
|
||||
|
||||
// $ExpectType readonly Ref<Book>[]
|
||||
const singleIteratee = booksQuerySet
|
||||
.orderBy('title')
|
||||
.orderBy(book => book.publisher, 'desc')
|
||||
.orderBy(book => book.title, false)
|
||||
.orderBy('publisher', 'asc')
|
||||
.orderBy('publisher', true)
|
||||
.toRefArray();
|
||||
|
||||
// $ExpectType readonly Ref<Book>[]
|
||||
const arrayIteratee = booksQuerySet
|
||||
.orderBy(['title'], ['asc'])
|
||||
.orderBy(['publisher', 'title'], [true, 'desc'])
|
||||
.orderBy([book => book.title], ['desc'])
|
||||
.orderBy(['title'])
|
||||
.orderBy([book => book.title, 'publisher'], ['desc', false])
|
||||
.toRefArray();
|
||||
|
||||
const invalidSingleKeyIteratee = booksQuerySet.orderBy('notABookPropertyKey'); // $ExpectError
|
||||
const invalidSingleFunctionIteratee = booksQuerySet.orderBy([book => book.notABookPropertyKey], false); // $ExpectError
|
||||
const invalidStringOrderDirectionType = booksQuerySet.orderBy('title', 'inc'); // $ExpectError
|
||||
const invalidSingleOrderDirectionType = booksQuerySet.orderBy('title', 4); // $ExpectError
|
||||
const invalidArrayKeyIteratee = booksQuerySet.orderBy(['notABookPropertyKey']); // $ExpectError
|
||||
const invalidArrayFunctionIteratee = booksQuerySet.orderBy([book => book.notABookPropertyKey]); // $ExpectError
|
||||
const invalidArrayStringOrderDirection = booksQuerySet.orderBy(['title'], ['inc']); // $ExpectError
|
||||
const invalidArrayOrderDirectionType = booksQuerySet.orderBy(['title'], [4]); // $ExpectError
|
||||
};
|
||||
|
||||
const selectors = () => {
|
||||
// test fixture, use reselect.createSelector in production code
|
||||
const createSelector = <S, OS extends OrmState<any>, Result extends any>(
|
||||
param1Creator: (state: S) => OS,
|
||||
combiner: (param1: OS) => Result
|
||||
): ((state: S) => Result) => state => combiner(param1Creator(state));
|
||||
|
||||
const orm = ormFixture();
|
||||
|
||||
const ormSelector = createOrmSelector(orm, session => session.Book.all().toRefArray()[0]);
|
||||
|
||||
interface RootState {
|
||||
db: OrmState<Schema>;
|
||||
}
|
||||
|
||||
const selector = createSelector<RootState, OrmState<Schema>, Ref<Book>>(
|
||||
({ db }) => db,
|
||||
ormSelector
|
||||
);
|
||||
|
||||
createSelector<RootState, OrmState<Schema>, Ref<Person>>(
|
||||
({ db }) => db,
|
||||
ormSelector // $ExpectError
|
||||
);
|
||||
|
||||
selector({ db: orm.getEmptyState() }); // $ExpectType Ref<Book>
|
||||
};
|
||||
|
||||
25
types/redux-orm/redux.d.ts
vendored
Normal file
25
types/redux-orm/redux.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
import { IndexedModelClasses, ORM, OrmState } from './ORM';
|
||||
import { OrmSession } from './Session';
|
||||
|
||||
export interface ORMReducer<I extends IndexedModelClasses<any>, TAction extends any = any> {
|
||||
(state: OrmState<I> | undefined, action: TAction): OrmSession<I>;
|
||||
}
|
||||
|
||||
export type defaultUpdater<I extends IndexedModelClasses<any>, TAction extends any = any> = (
|
||||
session: OrmSession<I>,
|
||||
action: TAction
|
||||
) => void;
|
||||
|
||||
export function createReducer<I extends IndexedModelClasses<any>, TAction extends any = any>(
|
||||
orm: ORM<I>,
|
||||
updater?: defaultUpdater<I, TAction>
|
||||
): ORMReducer<I, TAction>;
|
||||
|
||||
export interface ORMSelector<I extends IndexedModelClasses<any>, Result extends any = any> {
|
||||
(session: OrmSession<I>, ...args: any[]): Result;
|
||||
}
|
||||
|
||||
export function createSelector<I extends IndexedModelClasses<any>, Result extends any = any>(
|
||||
orm: ORM<I>,
|
||||
ormSelector: ORMSelector<I, Result>
|
||||
): (state: OrmState<I>) => Result;
|
||||
@ -1,23 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"lib": ["es6"],
|
||||
"target": "es6",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"baseUrl": "../",
|
||||
"typeRoots": [
|
||||
"../"
|
||||
],
|
||||
"typeRoots": ["../"],
|
||||
"types": [],
|
||||
"noEmit": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"redux-orm-tests.ts"
|
||||
"redux-orm-tests.ts",
|
||||
"db/index.d.ts",
|
||||
"db/Database.d.ts",
|
||||
"db/Table.d.ts",
|
||||
"helpers.d.ts",
|
||||
"redux.d.ts",
|
||||
"fields.d.ts",
|
||||
"ORM.d.ts",
|
||||
"Model.d.ts",
|
||||
"QuerySet.d.ts",
|
||||
"Session.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user