stampit
Version:
Create objects from reusable, composable behaviors.
361 lines (320 loc) • 16.4 kB
TypeScript
// Type definitions for stampit 5
// Project: https://github.com/stampit-org/stampit, https://stampit.js.org
// Definitions by: Vasyl Boroviak <https://github.com/koresar>
// Harris Lummis <https://github.com/lummish>
// PopGoesTheWza <https://github.com/popgoesthewza>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.5
// Utility types
/**
* @internal Base type for all `methods`-like metadata.
* @template This The type to use for `this` within methods.
*/
interface MethodMap<This> {
[s: string]: ((this: This, ...args: any[]) => any) | {};
}
/** @internal A plain old JavaScript object created by a `Stamp`. */
type Pojo = object; // { [s: string]: any; }
/** @internal Base type for all `properties`-like metadata. */
// TODO: discriminate Array
type PropertyMap = object; // { [s: string]: any; }
/** @internal Signature common to every `Stamp`s. */
interface StampSignature {
(options?: PropertyMap, ...args: unknown[]): any;
compose: ComposeMethod & stampit.Descriptor<any, any>;
}
/**
* @internal Extracts the `Stamp` type.
* @template Original The type to extract the `Stamp` type from.
*/
type StampType<Original> = Original extends /* disallowed types */ [] | bigint
? never
: stampit.IsADescriptor<Original> extends true
? Original extends stampit.ExtendedDescriptor<infer Obj, infer Stamp>
? Stamp
: never
: unknown extends Original /* is any or unknown */
? stampit.Stamp<Original>
: Original extends StampSignature
? Original
: Original extends stampit.ExtendedDescriptor<infer Obj, any>
? stampit.Stamp<Obj>
: Original extends Pojo
? stampit.Stamp<Original> /*assume it is the object from a stamp object*/
: never;
/**
* @internal The type of the object produced by the `Stamp`.
* @template Original The type (either a `Stamp` or a `ExtendedDescriptor`) to get the object type from.
*/
type StampObjectType<Original> = Original /* disallowed types */ extends bigint | boolean | number | string | symbol
? never
: stampit.IsADescriptor<Original> extends true
? Original extends stampit.ExtendedDescriptor<infer Obj, any>
? Obj
: never
: unknown extends Original /* is any or unknown */
? Original
: Original extends StampSignature
? Original extends stampit.Stamp<infer Obj> /* extended stamps may require infering twice */
? Obj extends stampit.Stamp<infer Obj>
? Obj
: Obj
: any
: Original extends stampit.ExtendedDescriptor<infer Obj, any>
? Obj
: Original extends Pojo
? Original
: never;
/**
* A factory function to create plain object instances.
* @template Obj The object type that the `Stamp` will create.
*/
interface FactoryFunction<Obj> {
(options?: PropertyMap, ...args: any[]): StampObjectType<Obj>;
}
/**
* @internal Chainables `Stamp` additional methods
* @template Obj The object type that the `Stamp` will create.
*/
type StampChainables<Obj> = Chainables<StampObjectType<Obj>, StampType<Obj>>;
/**
* @internal Chainables `Stamp` additional methods
* @template Obj The object type that the `Stamp` will create.
* @template S The type of the `Stamp` (when extending a `Stamp`.)
*/
interface Chainables<Obj, S extends StampSignature> {
/**
* Add methods to the methods prototype. Creates and returns new Stamp. **Chainable**.
* @template This The type to use for `this` within methods.
* @param methods Object(s) containing map of method names and bodies for delegation.
*/
// tslint:disable-next-line: no-unnecessary-generics
methods<This = Obj>(...methods: Array<MethodMap<This>>): S;
/**
* Take a variable number of objects and shallow assign them to any future created instance of the Stamp. Creates and returns new Stamp. **Chainable**.
* @param objects Object(s) to shallow assign for each new object.
*/
properties(...objects: PropertyMap[]): S;
/**
* Take a variable number of objects and shallow assign them to any future created instance of the Stamp. Creates and returns new Stamp. **Chainable**.
* @param objects Object(s) to shallow assign for each new object.
*/
props(...objects: PropertyMap[]): S;
/**
* Take a variable number of objects and deeply merge them to any future created instance of the Stamp. Creates and returns a new Stamp. **Chainable**.
* @param deepObjects The object(s) to deeply merge for each new object.
*/
deepProperties(...deepObjects: PropertyMap[]): S;
/**
* Take a variable number of objects and deeply merge them to any future created instance of the Stamp. Creates and returns a new Stamp. **Chainable**.
* @param deepObjects The object(s) to deeply merge for each new object.
*/
deepProps(...deepObjects: PropertyMap[]): S;
/**
* Take in a variable number of functions and add them to the initializers prototype as initializers. **Chainable**.
* @param functions Initializer functions used to create private data and privileged methods.
*/
initializers(...functions: Array<stampit.Initializer<Obj, S>>): S;
initializers(functions: Array<stampit.Initializer<Obj, S>>): S;
/**
* Take in a variable number of functions and add them to the initializers prototype as initializers. **Chainable**.
* @param functions Initializer functions used to create private data and privileged methods.
*/
init(...functions: Array<stampit.Initializer<Obj, S>>): S;
init(functions: Array<stampit.Initializer<Obj, S>>): S;
/**
* Take n objects and add them to a new stamp and any future stamp it composes with. Creates and returns new Stamp. **Chainable**.
* @param statics Object(s) containing map of property names and values to mixin into each new stamp.
*/
staticProperties(...statics: PropertyMap[]): S;
/**
* Take n objects and add them to a new stamp and any future stamp it composes with. Creates and returns new Stamp. **Chainable**.
* @param statics Object(s) containing map of property names and values to mixin into each new stamp.
*/
statics(...statics: PropertyMap[]): S;
/**
* Deeply merge a variable number of objects and add them to a new stamp and any future stamp it composes. Creates and returns a new Stamp. **Chainable**.
* @param deepStatics The object(s) containing static properties to be merged.
*/
staticDeepProperties(...deepStatics: PropertyMap[]): S;
/**
* Deeply merge a variable number of objects and add them to a new stamp and any future stamp it composes. Creates and returns a new Stamp. **Chainable**.
* @param deepStatics The object(s) containing static properties to be merged.
*/
deepStatics(...deepStatics: PropertyMap[]): S;
/**
* Take in a variable number of functions and add them to the composers prototype as composers. **Chainable**.
* @param functions Composer functions that will run in sequence while creating a new stamp from a list of composables. The resulting stamp and the composables get passed to composers.
*/
composers(...functions: Array<stampit.Composer<S>>): S;
composers(functions: Array<stampit.Composer<S>>): S;
/**
* Shallowly assign properties of Stamp arbitrary metadata and add them to a new stamp and any future Stamp it composes. Creates and returns a new Stamp. **Chainable**.
* @param confs The object(s) containing metadata properties.
*/
configuration(...confs: PropertyMap[]): S;
/**
* Shallowly assign properties of Stamp arbitrary metadata and add them to a new stamp and any future Stamp it composes. Creates and returns a new Stamp. **Chainable**.
* @param confs The object(s) containing metadata properties.
*/
conf(...confs: PropertyMap[]): S;
/**
* Deeply merge properties of Stamp arbitrary metadata and add them to a new Stamp and any future Stamp it composes. Creates and returns a new Stamp. **Chainable**.
* @param deepConfs The object(s) containing metadata properties.
*/
deepConfiguration(...deepConfs: PropertyMap[]): S;
/**
* Deeply merge properties of Stamp arbitrary metadata and add them to a new Stamp and any future Stamp it composes. Creates and returns a new Stamp. **Chainable**.
* @param deepConfs The object(s) containing metadata properties.
*/
deepConf(...deepConfs: PropertyMap[]): S;
/**
* Apply ES5 property descriptors to object instances created by the new Stamp returned by the function and any future Stamp it composes. Creates and returns a new stamp. **Chainable**.
* @param descriptors
*/
propertyDescriptors(...descriptors: PropertyDescriptorMap[]): S;
/**
* Apply ES5 property descriptors to a Stamp and any future Stamp it composes. Creates and returns a new stamp. **Chainable**.
* @param descriptors
*/
staticPropertyDescriptors(...descriptors: PropertyDescriptorMap[]): S;
}
/**
* A function which creates a new `Stamp`s from a list of `Composable`s.
* @template Obj The type of the object instance being produced by the `Stamp` or the type of the `Stamp` being created (when extending a `Stamp`.)
*/
type ComposeMethod = typeof stampit;
/**
* A function which creates a new `Stamp`s from a list of `Composable`s.
* @template Obj The type of the object instance being created by the `Stamp` or the type of the `Stamp` being created (when extending a `Stamp`.)
*/
// tslint:disable-next-line: no-unnecessary-generics
declare function stampit<Obj = any>(...composables: stampit.Composable[]): StampType<Obj>;
declare namespace stampit {
/** A composable object (either a `Stamp` or a `ExtendedDescriptor`.) */
type Composable = StampSignature | ExtendedDescriptor<any, any>;
/**
* A `Stamp`'s metadata.
* @template Obj The type of the object instance being produced by the `Stamp`.
* @template S The type of the `Stamp` (when extending a `Stamp`.)
*/
interface Descriptor<Obj, S extends StampSignature = Stamp<Obj>> {
/** A set of methods that will be added to the object's delegate prototype. */
methods?: MethodMap<Obj>;
/** A set of properties that will be added to new object instances by assignment. */
properties?: PropertyMap;
/** A set of properties that will be added to new object instances by deep property merge. */
deepProperties?: PropertyMap;
/** A set of object property descriptors (`PropertyDescriptor`) used for fine-grained control over object property behaviors. */
propertyDescriptors?: PropertyDescriptorMap;
/** A set of static properties that will be copied by assignment to the `Stamp`. */
staticProperties?: PropertyMap;
/** A set of static properties that will be added to the `Stamp` by deep property merge. */
staticDeepProperties?: PropertyMap;
/** A set of object property descriptors (`PropertyDescriptor`) to apply to the `Stamp`. */
staticPropertyDescriptors?: PropertyDescriptorMap;
/** An array of functions that will run in sequence while creating an object instance from a `Stamp`. `Stamp` details and arguments get passed to initializers. */
initializers?: Initializer<Obj, S> | Array<Initializer<Obj, S>>;
/** An array of functions that will run in sequence while creating a new `Stamp` from a list of `Composable`s. The resulting `Stamp` and the `Composable`s get passed to composers. */
composers?: Array<Composer<S>>;
/** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be copied by assignment. */
configuration?: PropertyMap;
/** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be deep merged. */
deepConfiguration?: PropertyMap;
}
/**
* A `stampit`'s metadata.
* @template Obj The type of the object instance being produced by the `Stamp`.
* @template S The type of the `Stamp` (when extending a `Stamp`.)
*/
interface ExtendedDescriptor<Obj, S extends StampSignature = Stamp<Obj>> extends Descriptor<Obj, S> {
/** A set of properties that will be added to new object instances by assignment. */
props?: PropertyMap;
/** A set of properties that will be added to new object instances by deep property merge. */
deepProps?: PropertyMap;
/** A set of static properties that will be copied by assignment to the `Stamp`. */
statics?: PropertyMap;
/** A set of static properties that will be added to the `Stamp` by deep property merge. */
deepStatics?: PropertyMap;
/** An array of functions that will run in sequence while creating an object instance from a `Stamp`. `Stamp` details and arguments get passed to initializers. */
init?: Initializer<Obj, S> | Array<Initializer<Obj, S>>;
/** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be copied by assignment. */
conf?: PropertyMap;
/** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be deep merged. */
deepConf?: PropertyMap;
/** The `name` property of the `Stamp`. Will be assigned to `MyStamp.name`. */
name?: string;
}
/**
* @internal Checks that a type is a ExtendedDescriptor (except `any` and `unknown`).
* @template Type A type to check if a ExtendedDescriptor.
*/
// TODO: Improve test by checking the type of common keys
type IsADescriptor<Type> = unknown extends Type
? keyof Type extends never
? false
: keyof Type extends infer K
? K extends keyof ExtendedDescriptor<unknown>
? true
: false
: false
: false;
/**
* A function used as `.initializers` argument.
* @template Obj The type of the object instance being produced by the `Stamp`.
* @template S The type of the `Stamp` producing the instance.
*/
interface Initializer<Obj, S extends StampSignature> {
(this: Obj, options: /*_propertyMap*/ any, context: InitializerContext<Obj, S>): void | Obj;
}
/**
* The `Initializer` function context.
* @template Obj The type of the object instance being produced by the `Stamp`.
* @template S The type of the `Stamp` producing the instance.
*/
interface InitializerContext<Obj, S extends StampSignature> {
/** The object instance being produced by the `Stamp`. If the initializer returns a value other than `undefined`, it replaces the instance. */
instance: Obj;
/** A reference to the `Stamp` producing the instance. */
stamp: S;
/** An array of the arguments passed into the `Stamp`, including the options argument. */
// ! above description from the specification is obscure
args: any[];
}
/**
* A function used as `.composers` argument.
* @template S The type of the `Stamp` produced by the `.compose()` method.
*/
interface Composer<S extends StampSignature> {
(parameters: ComposerParameters<S>): void | S;
}
/**
* The parameters received by the current `.composers` function.
* @template S The type of the `Stamp` produced by the `.compose()` method.
*/
interface ComposerParameters<S extends StampSignature> {
/** The result of the `Composable`s composition. */
stamp: S;
/** The list of composables the `Stamp` was just composed of. */
composables: Composable[];
}
/**
* A factory function to create plain object instances.
*
* It also has a `.compose()` method which is a copy of the `ComposeMethod` function and a `.compose` accessor to its `Descriptor`.
* @template Obj The object type that the `Stamp` will create.
*/
interface Stamp<Obj> extends FactoryFunction<Obj>, StampChainables<Obj>, StampSignature {
/** Just like calling stamp(), stamp.create() invokes the stamp and returns a new instance. */
create: FactoryFunction<Obj>;
/**
* A function which creates a new `Stamp`s from a list of `Composable`s.
* @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created.
*/
compose: ComposeMethod & Descriptor<StampObjectType<Obj>>;
}
/** A function which creates a new `Stamp`s from a list of `Composable`s. */
const compose: ComposeMethod;
}
// tslint:disable-next-line: npm-naming
export default stampit;