From d77ee72756aace5c5b5178c46b2dedc3f9c920df Mon Sep 17 00:00:00 2001 From: PopGoesTheWza <32041843+PopGoesTheWza@users.noreply.github.com> Date: Wed, 30 Oct 2019 15:35:36 +0100 Subject: [PATCH] stampit 4.3 - better typing through generics (#39188) * stampit 4.3 * fix `Error: stampit: Older version 3 must have a path mapping for itself.` * fix`baseUrl` & `typeRoots` * Some dtslint compliance * header fix * enhance * tests * tslint:disable * naming * fix refs/props changes + ExtendedDescriptor * more $ExpectType tests * Tentative `MethodMap` fix * Updated `MethodMap`tests * additional testing (failing) * fix `Composable` signature --- types/stampit/index.d.ts | 906 ++++++++++++++++-------------- types/stampit/stampit-tests.ts | 215 ++++++- types/stampit/tsconfig.json | 2 +- types/stampit/tslint.json | 2 +- types/stampit/v3/index.d.ts | 518 +++++++++++++++++ types/stampit/v3/stampit-tests.ts | 163 ++++++ types/stampit/v3/tsconfig.json | 28 + types/stampit/v3/tslint.json | 3 + 8 files changed, 1387 insertions(+), 450 deletions(-) create mode 100644 types/stampit/v3/index.d.ts create mode 100644 types/stampit/v3/stampit-tests.ts create mode 100644 types/stampit/v3/tsconfig.json create mode 100644 types/stampit/v3/tslint.json diff --git a/types/stampit/index.d.ts b/types/stampit/index.d.ts index e50aee21bc..5902ae555a 100644 --- a/types/stampit/index.d.ts +++ b/types/stampit/index.d.ts @@ -1,518 +1,566 @@ -// Type definitions for stampit 3.0 +// Type definitions for stampit 4.3 // Project: https://github.com/stampit-org/stampit, https://stampit.js.org // Definitions by: Vasyl Boroviak // Harris Lummis +// PopGoesTheWza // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 3.5 + +// Utility types /** - * Function used as .init() argument. + * @internal Base type for all `methods`-like metadata. + * @template This The type to use for `this` within methods. */ -type Init = (factoryArg: any, ctx?: Context) => any; - -/** - * Composer function - */ -type Composer = ({ stamp, composables }: { stamp: stampit.Stamp; composables: Composable[] }) => any; - -/** The stamp Descriptor */ -interface Descriptor { - /** Create a new stamp based on this descriptor */ - (...composables: Composable[]): stampit.Stamp; - /** - * A hash containing methods (functions) of any future created instance. - */ - methods?: {}; - /** - * Initialization function(s) which will be called per each newly created - * instance. - */ - initializers?: Init[]; - /** - * Properties which will shallowly copied into any future created instance. - */ - properties?: {}; - /** - * Properties which will be mixed to the new and any other stamp which this stamp will be composed with. - */ - staticProperties?: {}; - /** Deeply merged properties of object instances */ - deepProperties?: {}; - /** ES5 Property Descriptors applied to object instances */ - propertyDescriptors?: {}; - /** Deeply merged properties of Stamps */ - staticDeepProperties?: {}; - /** ES5 Property Descriptors applied to Stamps */ - staticPropertyDescriptors?: {}; - /** A configuration object to be shallowly assigned to Stamps */ - configuration?: {}; - /** A configuration object to be deeply merged to Stamps */ - deepConfiguration?: {}; +interface MethodMap { + [s: string]: ((this: This, ...args: any[]) => any) | {}; } -/** Any composable object (stamp or descriptor) */ -type Composable = stampit.Stamp | Descriptor; +/** @internal A plain old JavaScript object created by a `Stamp`. */ +type Pojo = object; // { [s: string]: any; } -/** - * The .init() function argument. - */ -interface Context { - /** - * The object which has been just instantiated. - */ - instance: any; +/** @internal Base type for all `properties`-like metadata. */ +// TODO: discriminate Array +type PropertyMap = object; // { [s: string]: any; } - /** - * The stamp the object has been instantiated with. - */ - stamp: stampit.Stamp; - - /** - * The arguments list passed to the stamp. - */ - args: any[]; +/** @internal Signature common to every `Stamp`s. */ +interface StampSignature { + (options?: PropertyMap, ...args: unknown[]): any; + compose: ComposeMethod & stampit.Descriptor; } -interface Options { - /** - * A hash containing methods (functions) of any future created instance. - */ - methods?: {}; - /** - * Initialization function(s) which will be called per each newly created - * instance. - */ - init?: Init | Init[]; - /** - * Initialization function(s) which will be called per each newly created - * instance. - */ - initializers?: Init | Init[]; - /** - * Properties which will shallowly copied into any future created instance. - */ - props?: {}; - /** - * Properties which will shallowly copied into any future created instance. - */ - properties?: {}; - /** - * A hash containing references to the object. This hash will be shallow mixed into any future created instance. - */ - refs?: {}; - /** - * Properties which will be mixed to the new and any other stamp which this - * stamp will be composed with. - */ - staticProperties?: {}; - /** - * Properties which will be mixed to the new and any other stamp which this - * stamp will be composed with. - */ - statics?: {}; - /** Deeply merged properties of object instances */ - deepProperties?: {}; - /** Deeply merged properties of object instances */ - deepProps?: {}; - /** ES5 Property Descriptors applied to object instances */ - propertyDescriptors?: {}; - /** Deeply merged properties of Stamps */ - staticDeepProperties?: {}; - /** Deeply merged properties of Stamps */ - deepStatics?: {}; - /** ES5 Property Descriptors applied to Stamps */ - staticPropertyDescriptors?: {}; - /** A configuration object to be shallowly assigned to Stamps */ - configuration?: {}; - /** A configuration object to be shallowly assigned to Stamps */ - conf?: {}; - /** A configuration object to be deeply merged to Stamps */ - deepConfiguration?: {}; - /** A configuration object to be deeply merged to Stamps */ - deepConf?: {}; - /** Callback functions to execute each time a composition occurs */ - composers?: Composer[]; -} - -/** Stampit Composable for main stampit() function */ -type StampitComposable = stampit.Stamp | Descriptor | Options; +/** + * @internal Extracts the `Stamp` type. + * @template Original The type to extract the `Stamp` type from. + */ +type StampType = Original extends /* disallowed types */ [] | bigint + ? never + : stampit.IsADescriptor extends true + ? (Original extends stampit.ExtendedDescriptor ? Stamp : never) + : unknown extends Original /* is any or unknown */ + ? stampit.Stamp + : Original extends StampSignature + ? Original + : Original extends stampit.ExtendedDescriptor + ? stampit.Stamp + : Original extends Pojo + ? stampit.Stamp /*assume it is the object from a stamp object*/ + : never; /** - * Return a factory (aka Stamp) function that will produce new objects using the - * prototypes that are passed in or composed. - * @param options Stampit options object containing refs, methods, - * init, props, statics, configurations, and property descriptors. + * @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. */ -declare function stampit(...composables: StampitComposable[]): stampit.Stamp; +type StampObjectType = Original extends /* disallowed types */ bigint | boolean | number | string | symbol + ? never + : stampit.IsADescriptor extends true + ? (Original extends stampit.ExtendedDescriptor ? Obj : never) + : unknown extends Original /* is any or unknown */ + ? Original + : Original extends StampSignature + ? (Original extends stampit.Stamp /* extended stamps may require infering twice */ + ? (Obj extends stampit.Stamp ? Obj : Obj) + : any) + : Original extends stampit.ExtendedDescriptor + ? 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 { + (options?: PropertyMap, ...args: any[]): StampObjectType; +} + +/** + * @internal Chainables `Stamp` additionnal methods + * @template Obj The object type that the `Stamp` will create. + */ +type StampChainables = Chainables, StampType>; + +/** + * @internal Chainables `Stamp` additionnal methods + * @template Obj The object type that the `Stamp` will create. + * @template S̤t̤a̤m̤p̤ The type of the `Stamp` (when extending a `Stamp`.) + */ +interface Chainables { + /** + * 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(...methods: Array>): S̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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>): S̤t̤a̤m̤p̤; + initializers(functions: Array>): S̤t̤a̤m̤p̤; + + /** + * 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>): S̤t̤a̤m̤p̤; + init(functions: Array>): S̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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>): S̤t̤a̤m̤p̤; + composers(functions: Array>): S̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; + + /** + * 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̤t̤a̤m̤p̤; +} + +/** + * 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(...composables: stampit.Composable[]): StampType; declare namespace stampit { + /** A composable object (either a `Stamp` or a `ExtendedDescriptor`.) */ + type Composable = StampSignature | ExtendedDescriptor; + /** - * A factory function that will produce new objects using the - * prototypes that are passed in or composed. + * A `Stamp`'s metadata. + * @template Obj The type of the object instance being produced by the `Stamp`. + * @template S̤t̤a̤m̤p̤ The type of the `Stamp` (when extending a `Stamp`.) */ - interface Stamp { - /** - * Invokes the stamp and returns a new object instance. - * @param state Properties you wish to set on the new objects. - * @param encloseArgs The remaining arguments are passed to all .enclose() functions. - * WARNING Avoid using two different .enclose() functions that expect different arguments. - * .enclose() functions that take arguments should not be considered safe to compose - * with other .enclose() functions that also take arguments. Taking arguments with - * an .enclose() function is an anti-pattern that should be avoided, when possible. - * @return A new object composed of the Stamps and prototypes provided. - */ - (state?: {}, ...encloseArgs: any[]): any; - - /** - * Just like calling stamp(), stamp.create() invokes the stamp and returns a new instance. - * @param state Properties you wish to set on the new objects. - * @param encloseArgs The remaining arguments are passed to all .enclose() functions. - * WARNING Avoid using two different .enclose() functions that expect different arguments. - * .enclose() functions that take arguments should not be considered safe to compose - * with other .enclose() functions that also take arguments. Taking arguments with - * an .enclose() function is an anti-pattern that should be avoided, when possible. - * @return A new object composed of the Stamps and prototypes provided. - */ - create(state?: {}, ...encloseArgs: any[]): any; - - /** - * Stamp metadata/composer function - */ - compose: Descriptor; - - /** - * Add methods to the methods prototype. Creates and returns new Stamp. Chainable. - * @param methods Object(s) containing map of method names and bodies for delegation. - * @return A new Stamp. - */ - methods(...methods: Array<{}>): Stamp; - - /** - * Take n objects and add them to the state prototype. Creates and returns new Stamp. Chainable. - * @param states Object(s) containing map of property names and values to clone for each new object. - * @return A new Stamp. - */ - refs(...states: Array<{}>): Stamp; - - /** - * 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. - * @return A new Stamp. - */ - props(...objects: Array<{}>): Stamp; - - /** - * 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. - * @return A new Stamp. - */ - properties(...objects: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - deepProps(...deepObjects: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - deepProperties(...deepObjects: Array<{}>): Stamp; - - /** - * @deprecated Use .init() instead. - */ - enclose(...functions: Init[]): Stamp; - - /** - * @deprecated Use .init() instead. - */ - enclose(...functions: Array<{}>): Stamp; - - /** - * Take in a variable number of functions and add them to the enclose - * prototype as initializers. - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp - */ - init(...functions: Init[]): Stamp; - - /** - * Take in a variable number of functions and add them to the enclose - * prototype as initializers. - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp - */ - init(functions: Init[]): Stamp; - - /** - * Take in a variable number of functions and add them to the enclose - * prototype as initializers. - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp - */ - initializers(...functions: Init[]): Stamp; - - /** - * Take in a variable number of functions and add them to the enclose - * prototype as initializers. - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp - */ - initializers(functions: Init[]): Stamp; - - /** - * 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. - * @return A new Stamp. - */ - statics(...statics: Array<{}>): Stamp; - - /** - * 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. - * @return A new Stamp. - */ - staticProperties(...statics: Array<{}>): Stamp; - - /** - * 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 - * @returns A new stamp - */ - deepStatics(...deepStatics: Array<{}>): Stamp; - - /** - * 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 - * @returns A new stamp - */ - staticDeepProperties(...deepStatics: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - conf(...confs: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - configuration(...confs: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - deepConf(...deepConfs: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - deepConfiguration(...deepConfs: Array<{}>): Stamp; - - /** - * 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 - * @returns A new Stamp - */ - propertyDescriptors(...descriptors: Array<{}>): Stamp; - - /** - * Apply ES5 property descriptors to a Stamp and any future Stamp it - * composes. Creates and returns a new stamp. Chainable. - * @param descriptors - * @returns A new Stamp - */ - staticPropertyDescriptors(...descriptors: Array<{}>): Stamp; + interface Descriptor> { + /** A set of methods that will be added to the object's delegate prototype. */ + methods?: MethodMap; + /** 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 /* & ThisType */; + /** A set of static properties that will be added to the `Stamp` by deep property merge. */ + staticDeepProperties?: PropertyMap /* & ThisType */; + /** A set of object property descriptors (`PropertyDescriptor`) to apply to the `Stamp`. */ + staticPropertyDescriptors?: PropertyDescriptorMap /* & ThisType */; + /** 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 | Array>; + /** 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>; + /** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be copied by assignment. */ + configuration?: PropertyMap /* & ThisType */; + /** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be deep merged. */ + deepConfiguration?: PropertyMap /* & ThisType */; } + /** - * A shortcut methods for stampit().methods() + * A `stampit`'s metadata. + * @template Obj The type of the object instance being produced by the `Stamp`. + * @template S̤t̤a̤m̤p̤ The type of the `Stamp` (when extending a `Stamp`.) + */ + interface ExtendedDescriptor> extends Descriptor { + /** 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 /* & ThisType */; + /** A set of static properties that will be added to the `Stamp` by deep property merge. */ + deepStatics?: PropertyMap /* & ThisType */; + /** 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 | Array>; + /** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be copied by assignment. */ + conf?: PropertyMap /* & ThisType */; + /** A set of options made available to the `Stamp` and its initializers during object instance creation. These will be deep merged. */ + deepConf?: PropertyMap /* & ThisType */; + // TODO: Add description + 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 = unknown extends Type + ? (keyof Type extends never + ? false + : keyof Type extends infer K + ? (K extends keyof ExtendedDescriptor ? 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̤t̤a̤m̤p̤ The type of the `Stamp` producing the instance. + */ + interface Initializer { + (this: Obj, options: /*_propertyMap*/ any, context: InitializerContext): void | Obj; + } + + /** + * The `Initializer` function context. + * @template Obj The type of the object instance being produced by the `Stamp`. + * @template S̤t̤a̤m̤p̤ The type of the `Stamp` producing the instance. + */ + interface InitializerContext { + /** 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̤t̤a̤m̤p̤; + /** 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̤t̤a̤m̤p̤ The type of the `Stamp` produced by the `.compose()` method. + */ + interface Composer { + (parameters: ComposerParameters): void | S̤t̤a̤m̤p̤; + } + + /** + * The parameters received by the current `.composers` function. + * @template S̤t̤a̤m̤p̤ The type of the `Stamp` produced by the `.compose()` method. + */ + interface ComposerParameters { + /** The result of the `Composable`s composition. */ + stamp: S̤t̤a̤m̤p̤; + /** 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 extends FactoryFunction, StampChainables, StampSignature { + /** Just like calling stamp(), stamp.create() invokes the stamp and returns a new instance. */ + create: FactoryFunction; + + /** + * 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>; + } + + /** + * A shortcut method for stampit().methods() + * + * Add methods to the methods prototype. Creates and returns new Stamp. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @template This The type to use for `this` within methods. * @param methods Object(s) containing map of method names and bodies for delegation. - * @return A new Stamp. */ - function methods(...methods: Array<{}>): Stamp; - - /** - * A shortcut methods for stampit().refs() - * @param states Object(s) containing map of property names and values to clone for each new object. - * @return A new Stamp. - */ - function refs(...states: Array<{}>): Stamp; - - /** - * A shortcut method for stampit().props() - * @param objects Object(s) to shallow assign for each new object. - * @return A new Stamp. - */ - function props(...objects: Array<{}>): Stamp; + function methods(this: StampObjectType, ...methods: Array>): StampType; /** * A shortcut method for stampit().properties() + * + * Take a variable number of objects and shallow assign them to any future created instance of the Stamp. Creates and returns new Stamp. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param objects Object(s) to shallow assign for each new object. - * @return A new Stamp. */ - function properties(...objects: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function properties(...objects: PropertyMap[]): StampType; /** - * A shortcut method for stampit().deepProps() - * @param deepObjects The object(s) to deeply merge for each new object - * @returns A new Stamp + * A shortcut method for stampit().props() + * + * Take a variable number of objects and shallow assign them to any future created instance of the Stamp. Creates and returns new Stamp. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param objects Object(s) to shallow assign for each new object. */ - function deepProps(...deepObjects: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function props(...objects: PropertyMap[]): StampType; /** * A shortcut method for stampit().deepProperties() + * + * Take a variable number of objects and deeply merge them to any future created instance of the Stamp. Creates and returns a new Stamp. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param deepObjects The object(s) to deeply merge for each new object - * @returns A new Stamp */ - function deepProperties(...deepObjects: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function deepProperties(...deepObjects: PropertyMap[]): StampType; /** - * A shortcut method for stampit().init() - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp + * A shortcut method for stampit().deepProps() + * + * Take a variable number of objects and deeply merge them to any future created instance of the Stamp. Creates and returns a new Stamp. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param deepObjects The object(s) to deeply merge for each new object */ - function init(...functions: Init[]): Stamp; - - /** - * A shortcut method for stampit().init() - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp - */ - function init(functions: Init[]): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function deepProps(...deepObjects: PropertyMap[]): StampType; /** * A shortcut method for stampit().initializers() - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp + * + * Take in a variable number of functions and add them to the initializers prototype as initializers. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param functions Initializer functions used to create private data and privileged methods */ - function initializers(...functions: Init[]): Stamp; + function initializers>( + // tslint:disable-next-line: no-unnecessary-generics + ...functions: Array, S̤t̤a̤m̤p̤>> + ): S̤t̤a̤m̤p̤; + function initializers>( + // tslint:disable-next-line: no-unnecessary-generics + functions: Array, S̤t̤a̤m̤p̤>>, + ): S̤t̤a̤m̤p̤; /** - * A shortcut method for stampit().initializers() - * @param functions Initializer functions used to create private data and - * privileged methods - * @returns A new stamp + * A shortcut method for stampit().init() + * + * Take in a variable number of functions and add them to the initializers prototype as initializers. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param functions Initializer functions used to create private data and privileged methods */ - function initializers(functions: Init[]): Stamp; + function init>( + // tslint:disable-next-line: no-unnecessary-generics + ...functions: Array, S̤t̤a̤m̤p̤>> + ): S̤t̤a̤m̤p̤; + function init>( + // tslint:disable-next-line: no-unnecessary-generics + functions: Array, S̤t̤a̤m̤p̤>>, + ): S̤t̤a̤m̤p̤; /** * A shortcut method for stampit().statics() + * + * Take n objects and add them to a new stamp and any future stamp it composes with. Creates and returns new Stamp. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param statics Object(s) containing map of property names and values to mixin into each new stamp. - * @return A new Stamp. */ - function statics(...statics: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function staticProperties(...statics: PropertyMap[]): StampType; /** * A shortcut method for stampit().staticProperties() + * + * Take n objects and add them to a new stamp and any future stamp it composes with. Creates and returns new Stamp. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param statics Object(s) containing map of property names and values to mixin into each new stamp. - * @return A new Stamp. */ - function staticProperties(...statics: Array<{}>): Stamp; - - /** - * A shortcut method for stampit().deepStatics() - * @param deepStatics The object(s) containing static properties to be - * merged - * @returns A new stamp - */ - function deepStatics(...deepStatics: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function statics(...statics: PropertyMap[]): StampType; /** * A shortcut method for stampit().staticDeepProperties() - * @param deepStatics The object(s) containing static properties to be - * merged - * @returns A new stamp + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param deepStatics The object(s) containing static properties to be merged */ - function staticDeepProperties(...deepStatics: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function staticDeepProperties(...deepStatics: PropertyMap[]): StampType; /** - * A shortcut method for stampit().conf() - * @param confs The object(s) containing metadata properties - * @returns A new Stamp + * A shortcut method for stampit().deepStatics() + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param deepStatics The object(s) containing static properties to be merged */ - function conf(...confs: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function deepStatics(...deepStatics: PropertyMap[]): StampType; + + /** + * A shortcut method for stampit().composers() + * + * Take in a variable number of functions and add them to the composers prototype as composers. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @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. + */ + function composers(...functions: Array>>): StampType; + function composers(functions: Array>>): StampType; /** * A shortcut method for stampit().configuration() + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param confs The object(s) containing metadata properties - * @returns A new Stamp */ - function configuration(...confs: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function configuration(...confs: PropertyMap[]): StampType; /** - * A shortcut method for stampit().deepConf() - * @param deepConfs The object(s) containing metadata properties - * @returns A new Stamp + * A shortcut method for stampit().conf() + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param confs The object(s) containing metadata properties */ - function deepConf(...deepConfs: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function conf(...confs: PropertyMap[]): StampType; /** * A shortcut method for stampit().deepConfiguration() + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param deepConfs The object(s) containing metadata properties - * @returns A new Stamp */ - function deepConfiguration(...deepConfs: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function deepConfiguration(...deepConfs: PropertyMap[]): StampType; + + /** + * A shortcut method for stampit().deepConf() + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. + * @param deepConfs The object(s) containing metadata properties + */ + // tslint:disable-next-line: no-unnecessary-generics + function deepConf(...deepConfs: PropertyMap[]): StampType; /** * A shortcut method for stampit().propertyDescriptors() + * + * 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**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param descriptors - * @returns A new Stamp */ - function propertyDescriptors(...descriptors: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function propertyDescriptors(...descriptors: PropertyDescriptorMap[]): StampType; /** * A shortcut method for stampit().staticPropertyDescriptors() + * + * Apply ES5 property descriptors to a Stamp and any future Stamp it composes. Creates and returns a new stamp. **Chainable**. + * @template Obj The type of the object instance being produced by the `Stamp`. or the type of the `Stamp` being created. * @param descriptors - * @returns A new Stamp */ - function staticPropertyDescriptors(...descriptors: Array<{}>): Stamp; + // tslint:disable-next-line: no-unnecessary-generics + function staticPropertyDescriptors(...descriptors: PropertyDescriptorMap[]): StampType; - /** - * Take two or more Composables and combine them to produce a new Stamp. - * Combining overrides properties with last-in priority. - * @param composables Composable objects used to create the stamp. - * @return A new Stamp made of all the given composables. - */ - function compose(...composables: Composable[]): Stamp; + /** A function which creates a new `Stamp`s from a list of `Composable`s. */ + const compose: ComposeMethod; + + /** the version of the NPM `stampit` package. */ + const version: string; } - -export = stampit; +export const compose: typeof stampit.compose; +export const composers: typeof stampit.composers; +export const conf: typeof stampit.conf; +export const configuration: typeof stampit.configuration; +export const deepConf: typeof stampit.deepConf; +export const deepConfiguration: typeof stampit.deepConfiguration; +export const deepProperties: typeof stampit.deepProperties; +export const deepProps: typeof stampit.deepProps; +export const deepStatics: typeof stampit.deepStatics; +export const init: typeof stampit.init; +export const initializers: typeof stampit.initializers; +export const methods: typeof stampit.methods; +export const properties: typeof stampit.properties; +export const propertyDescriptors: typeof stampit.propertyDescriptors; +export const props: typeof stampit.props; +export const staticDeepProperties: typeof stampit.staticDeepProperties; +export const staticProperties: typeof stampit.staticProperties; +export const staticPropertyDescriptors: typeof stampit.staticPropertyDescriptors; +export const version: typeof stampit.version; +// tslint:disable-next-line: npm-naming +export default stampit; diff --git a/types/stampit/stampit-tests.ts b/types/stampit/stampit-tests.ts index 6f527dcaac..9f3f45ec98 100644 --- a/types/stampit/stampit-tests.ts +++ b/types/stampit/stampit-tests.ts @@ -1,4 +1,31 @@ -import stampit = require('stampit'); +/** Import 'stampit' module */ +import stampit from 'stampit'; + +/** Import 'stampit' as ... module */ +// import * as Stamp from 'stampit'; + +/** selective import from module */ +// import { +// compose, +// composers, +// conf, +// configuration, +// deepConf, +// deepConfiguration, +// deepProperties, +// deepProps, +// deepStatics, +// init, +// initializers, +// methods, +// properties, +// propertyDescriptors, +// props, +// staticDeepProperties, +// staticProperties, +// staticPropertyDescriptors, +// version, +// } from 'stampit'; const a = stampit().init(function(options) { const a = options.args[0]; @@ -21,10 +48,16 @@ const foo = c(); // we won't throw this one away... foo.getA(); // "a" foo.getB(); // "b" -// Here's a mixin with public methods, and some refs: +// Here's a mixin with public methods, and some props: const membership = stampit({ methods: { - members: {}, + members: { + itCanBe: "anything", + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, add(member: any) { this.members[member.name] = member; return this; @@ -33,13 +66,13 @@ const membership = stampit({ return this.members[name]; } }, - refs: { + props: { members: {} } }); // Let's set some defaults: -const defaults = stampit().refs({ +const defaults = stampit().props({ name: 'The Saloon', specials: 'Whisky, Gin, Tequila' }); @@ -47,7 +80,7 @@ const defaults = stampit().refs({ // Classical inheritance has nothing on this. No parent/child coupling. No deep inheritance hierarchies. // Just good, clean code reusability. const bar = stampit.compose(defaults, membership); -// Note that you can override refs on instantiation: +// Note that you can override props on instantiation: const myBar = bar({ name: 'Moe\'s' }); // Silly, but proves that everything is as it should be. myBar.add({ name: 'Homer' }).open().getMember('Homer'); @@ -58,20 +91,28 @@ const myStamp = stampit().methods({ }, methodOverride() { return false; - } + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, }).methods({ bar() { return 'bar'; }, methodOverride() { return true; - } + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, }); -myStamp.props({ +myStamp.deepProps({ foo: { bar: 'bar' }, refsOverride: false -}).refs({ +}).props({ bar: 'bar', refsOverride: true }); @@ -93,19 +134,27 @@ myStamp.init(function() { let obj = myStamp.create(); obj.getSecret && obj.a && obj.b && obj.c; // true -const newStamp = stampit({ refs: { defaultNum: 1 } }).compose(myStamp); +const newStamp = stampit({ props: { defaultNum: 1 } }).compose(myStamp); const obj1 = stampit().methods({ a() { return 'a'; - } + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, }, { b() { return 'b'; - } + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, }).create(); -const obj2 = stampit().refs({ +const obj2 = stampit().props({ a: 'a' }, { b: 'b' @@ -125,7 +174,11 @@ Constructor.prototype.foo = function foo() { const newskool = stampit().methods({ bar: function bar() { return 'bar'; - } + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, // your methods here... }).init(function() { this.baz = 'baz'; @@ -149,15 +202,139 @@ interface SomeStampInstance { } // Test import of stamp type -interface SomeStamp extends stampit.Stamp { +interface SomeStamp extends stampit.Stamp { (params: { a: number; b: boolean}): SomeStampInstance; } -const SomeStamp = stampit() +const SomeStamp = stampit() .init(function(params: { a: number; b: boolean}) { - this.a = '' + a; + this.a = '' + a; // this SomeStampInstance this.b = '' + b; - }) as SomeStamp; + }); SomeStamp({ a: 1, b: false }); // $ExpectType SomeStampInstance SomeStamp({ a: 1, b: false }).a; // $ExpectType string + +const d: stampit.ExtendedDescriptor<{}> = { + methods: {}, + properties: {}, + deepProperties: {}, + propertyDescriptors: {}, + initializers: [], + staticProperties: {}, + staticDeepProperties: {}, + staticPropertyDescriptors: {}, + composers: [], + configuration: {}, + deepConfiguration: {}, + name: '', + // shorthands + props: {}, + deepProps: {}, + init: [], + statics: {}, + deepStatics: {}, + conf: {}, + deepConf: {}, +}; + +// The `.compose()` method +const compose = stampit.compose; // $ExpectType typeof stampit + +const stampUntyped = compose(); // $ExpectType Stamp +stampUntyped(); // $ExpectType any +// const stampAny = compose(); // $ExpectType Stamp +// stampAny(); // $ExpectType any +const stampBigint = compose(); // $ExpectType never +const stampBoolean = compose(); // $ExpectType never +const stampNull = compose(); // $ExpectType never +const stampNumber = compose(); // $ExpectType never +const stampString = compose(); // $ExpectType never +const stampSymbol = compose(); // $ExpectType never +const stampUndefined = compose(); // $ExpectType never +const stampUnknown = compose(); // $ExpectType Stamp +stampUnknown(); // $ExpectType unknown +const stampNever = compose(); // $ExpectType never + +// Declare interface of objects created by stamps +interface Object0 { + action: () => void; + value: number; +} + +interface Object1 { + reaction: () => void; + property: number; +} + +const stampObject0 = compose(); // $ExpectType Stamp +stampObject0(); // $ExpectType Object0 + +const stampObject1 = compose(); // $ExpectType Stamp +stampObject1(); // $ExpectType Object1 + +// Define a typed ExtendedDescriptor and benefit its properly typed `this` in `methods` +const descriptor0: stampit.ExtendedDescriptor = { + methods: { + logLocalThis() { + this; // $ExpectType Object0 + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, + }, + properties: {}, + deepProperties: {}, + propertyDescriptors: {}, + staticProperties: {}, + staticDeepProperties: {}, + staticPropertyDescriptors: {}, + initializers: [ + function(options, context) { + this; // $ExpectType Object0 + return context.instance; + } + ], + composers: [ + (parameters) => { + return parameters.stamp; + } + ], + configuration: {}, + deepConfiguration: {} +}; + +const stampDescriptor0 = compose(descriptor0); // $ExpectType Stamp +stampDescriptor0(); // $ExpectType Object0 + +// check typed stamps... with untyped descriptor (`this` isn't typed in `methods`) +// inline type assertion is still possible though +const stampUntypedDescriptor0 = compose(/* > */ { + methods: { + logLocalThis() { + this; // $ExpectType any + }, + myMembers: {}, + dbConnString: "pgs://data.local", + MY_LIMIT: 100, + myRegExp: /foobar/g, + }, +} /* as stampit.ExtendedDescriptor */); +stampUntypedDescriptor0; // $ExpectType Stamp +stampUntypedDescriptor0(); // $ExpectType Object0 + +// Check a stamp's `.compose()` method +const stamp1 = stampObject0.compose(); // $ExpectType Stamp +stamp1(); // $ExpectType Object1 + +// Define an extended stamp. +// The type of created object cannot be changed afterward +// Lazy interface composition can be done using the `&` intersection operator +interface ExtendedStamp extends stampit.Stamp { + decompose: () => unknown; +} + +// Invoke `.compose()` method with the type of the extended stamp +const extendedStamp0 = compose(); // $ExpectType ExtendedStamp +extendedStamp0(); // $ExpectType Object0 & Object1 diff --git a/types/stampit/tsconfig.json b/types/stampit/tsconfig.json index ee248728d6..ff86907793 100644 --- a/types/stampit/tsconfig.json +++ b/types/stampit/tsconfig.json @@ -20,4 +20,4 @@ "index.d.ts", "stampit-tests.ts" ] -} \ No newline at end of file +} diff --git a/types/stampit/tslint.json b/types/stampit/tslint.json index e60c15844f..f93cf8562a 100644 --- a/types/stampit/tslint.json +++ b/types/stampit/tslint.json @@ -1,3 +1,3 @@ { "extends": "dtslint/dt.json" -} \ No newline at end of file +} diff --git a/types/stampit/v3/index.d.ts b/types/stampit/v3/index.d.ts new file mode 100644 index 0000000000..e50aee21bc --- /dev/null +++ b/types/stampit/v3/index.d.ts @@ -0,0 +1,518 @@ +// Type definitions for stampit 3.0 +// Project: https://github.com/stampit-org/stampit, https://stampit.js.org +// Definitions by: Vasyl Boroviak +// Harris Lummis +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/** + * Function used as .init() argument. + */ +type Init = (factoryArg: any, ctx?: Context) => any; + +/** + * Composer function + */ +type Composer = ({ stamp, composables }: { stamp: stampit.Stamp; composables: Composable[] }) => any; + +/** The stamp Descriptor */ +interface Descriptor { + /** Create a new stamp based on this descriptor */ + (...composables: Composable[]): stampit.Stamp; + /** + * A hash containing methods (functions) of any future created instance. + */ + methods?: {}; + /** + * Initialization function(s) which will be called per each newly created + * instance. + */ + initializers?: Init[]; + /** + * Properties which will shallowly copied into any future created instance. + */ + properties?: {}; + /** + * Properties which will be mixed to the new and any other stamp which this stamp will be composed with. + */ + staticProperties?: {}; + /** Deeply merged properties of object instances */ + deepProperties?: {}; + /** ES5 Property Descriptors applied to object instances */ + propertyDescriptors?: {}; + /** Deeply merged properties of Stamps */ + staticDeepProperties?: {}; + /** ES5 Property Descriptors applied to Stamps */ + staticPropertyDescriptors?: {}; + /** A configuration object to be shallowly assigned to Stamps */ + configuration?: {}; + /** A configuration object to be deeply merged to Stamps */ + deepConfiguration?: {}; +} + +/** Any composable object (stamp or descriptor) */ +type Composable = stampit.Stamp | Descriptor; + +/** + * The .init() function argument. + */ +interface Context { + /** + * The object which has been just instantiated. + */ + instance: any; + + /** + * The stamp the object has been instantiated with. + */ + stamp: stampit.Stamp; + + /** + * The arguments list passed to the stamp. + */ + args: any[]; +} + +interface Options { + /** + * A hash containing methods (functions) of any future created instance. + */ + methods?: {}; + /** + * Initialization function(s) which will be called per each newly created + * instance. + */ + init?: Init | Init[]; + /** + * Initialization function(s) which will be called per each newly created + * instance. + */ + initializers?: Init | Init[]; + /** + * Properties which will shallowly copied into any future created instance. + */ + props?: {}; + /** + * Properties which will shallowly copied into any future created instance. + */ + properties?: {}; + /** + * A hash containing references to the object. This hash will be shallow mixed into any future created instance. + */ + refs?: {}; + /** + * Properties which will be mixed to the new and any other stamp which this + * stamp will be composed with. + */ + staticProperties?: {}; + /** + * Properties which will be mixed to the new and any other stamp which this + * stamp will be composed with. + */ + statics?: {}; + /** Deeply merged properties of object instances */ + deepProperties?: {}; + /** Deeply merged properties of object instances */ + deepProps?: {}; + /** ES5 Property Descriptors applied to object instances */ + propertyDescriptors?: {}; + /** Deeply merged properties of Stamps */ + staticDeepProperties?: {}; + /** Deeply merged properties of Stamps */ + deepStatics?: {}; + /** ES5 Property Descriptors applied to Stamps */ + staticPropertyDescriptors?: {}; + /** A configuration object to be shallowly assigned to Stamps */ + configuration?: {}; + /** A configuration object to be shallowly assigned to Stamps */ + conf?: {}; + /** A configuration object to be deeply merged to Stamps */ + deepConfiguration?: {}; + /** A configuration object to be deeply merged to Stamps */ + deepConf?: {}; + /** Callback functions to execute each time a composition occurs */ + composers?: Composer[]; +} + +/** Stampit Composable for main stampit() function */ +type StampitComposable = stampit.Stamp | Descriptor | Options; + +/** + * Return a factory (aka Stamp) function that will produce new objects using the + * prototypes that are passed in or composed. + * @param options Stampit options object containing refs, methods, + * init, props, statics, configurations, and property descriptors. + */ +declare function stampit(...composables: StampitComposable[]): stampit.Stamp; + +declare namespace stampit { + /** + * A factory function that will produce new objects using the + * prototypes that are passed in or composed. + */ + interface Stamp { + /** + * Invokes the stamp and returns a new object instance. + * @param state Properties you wish to set on the new objects. + * @param encloseArgs The remaining arguments are passed to all .enclose() functions. + * WARNING Avoid using two different .enclose() functions that expect different arguments. + * .enclose() functions that take arguments should not be considered safe to compose + * with other .enclose() functions that also take arguments. Taking arguments with + * an .enclose() function is an anti-pattern that should be avoided, when possible. + * @return A new object composed of the Stamps and prototypes provided. + */ + (state?: {}, ...encloseArgs: any[]): any; + + /** + * Just like calling stamp(), stamp.create() invokes the stamp and returns a new instance. + * @param state Properties you wish to set on the new objects. + * @param encloseArgs The remaining arguments are passed to all .enclose() functions. + * WARNING Avoid using two different .enclose() functions that expect different arguments. + * .enclose() functions that take arguments should not be considered safe to compose + * with other .enclose() functions that also take arguments. Taking arguments with + * an .enclose() function is an anti-pattern that should be avoided, when possible. + * @return A new object composed of the Stamps and prototypes provided. + */ + create(state?: {}, ...encloseArgs: any[]): any; + + /** + * Stamp metadata/composer function + */ + compose: Descriptor; + + /** + * Add methods to the methods prototype. Creates and returns new Stamp. Chainable. + * @param methods Object(s) containing map of method names and bodies for delegation. + * @return A new Stamp. + */ + methods(...methods: Array<{}>): Stamp; + + /** + * Take n objects and add them to the state prototype. Creates and returns new Stamp. Chainable. + * @param states Object(s) containing map of property names and values to clone for each new object. + * @return A new Stamp. + */ + refs(...states: Array<{}>): Stamp; + + /** + * 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. + * @return A new Stamp. + */ + props(...objects: Array<{}>): Stamp; + + /** + * 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. + * @return A new Stamp. + */ + properties(...objects: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + deepProps(...deepObjects: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + deepProperties(...deepObjects: Array<{}>): Stamp; + + /** + * @deprecated Use .init() instead. + */ + enclose(...functions: Init[]): Stamp; + + /** + * @deprecated Use .init() instead. + */ + enclose(...functions: Array<{}>): Stamp; + + /** + * Take in a variable number of functions and add them to the enclose + * prototype as initializers. + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + init(...functions: Init[]): Stamp; + + /** + * Take in a variable number of functions and add them to the enclose + * prototype as initializers. + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + init(functions: Init[]): Stamp; + + /** + * Take in a variable number of functions and add them to the enclose + * prototype as initializers. + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + initializers(...functions: Init[]): Stamp; + + /** + * Take in a variable number of functions and add them to the enclose + * prototype as initializers. + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + initializers(functions: Init[]): Stamp; + + /** + * 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. + * @return A new Stamp. + */ + statics(...statics: Array<{}>): Stamp; + + /** + * 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. + * @return A new Stamp. + */ + staticProperties(...statics: Array<{}>): Stamp; + + /** + * 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 + * @returns A new stamp + */ + deepStatics(...deepStatics: Array<{}>): Stamp; + + /** + * 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 + * @returns A new stamp + */ + staticDeepProperties(...deepStatics: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + conf(...confs: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + configuration(...confs: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + deepConf(...deepConfs: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + deepConfiguration(...deepConfs: Array<{}>): Stamp; + + /** + * 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 + * @returns A new Stamp + */ + propertyDescriptors(...descriptors: Array<{}>): Stamp; + + /** + * Apply ES5 property descriptors to a Stamp and any future Stamp it + * composes. Creates and returns a new stamp. Chainable. + * @param descriptors + * @returns A new Stamp + */ + staticPropertyDescriptors(...descriptors: Array<{}>): Stamp; + } + /** + * A shortcut methods for stampit().methods() + * @param methods Object(s) containing map of method names and bodies for delegation. + * @return A new Stamp. + */ + function methods(...methods: Array<{}>): Stamp; + + /** + * A shortcut methods for stampit().refs() + * @param states Object(s) containing map of property names and values to clone for each new object. + * @return A new Stamp. + */ + function refs(...states: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().props() + * @param objects Object(s) to shallow assign for each new object. + * @return A new Stamp. + */ + function props(...objects: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().properties() + * @param objects Object(s) to shallow assign for each new object. + * @return A new Stamp. + */ + function properties(...objects: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().deepProps() + * @param deepObjects The object(s) to deeply merge for each new object + * @returns A new Stamp + */ + function deepProps(...deepObjects: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().deepProperties() + * @param deepObjects The object(s) to deeply merge for each new object + * @returns A new Stamp + */ + function deepProperties(...deepObjects: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().init() + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + function init(...functions: Init[]): Stamp; + + /** + * A shortcut method for stampit().init() + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + function init(functions: Init[]): Stamp; + + /** + * A shortcut method for stampit().initializers() + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + function initializers(...functions: Init[]): Stamp; + + /** + * A shortcut method for stampit().initializers() + * @param functions Initializer functions used to create private data and + * privileged methods + * @returns A new stamp + */ + function initializers(functions: Init[]): Stamp; + + /** + * A shortcut method for stampit().statics() + * @param statics Object(s) containing map of property names and values to mixin into each new stamp. + * @return A new Stamp. + */ + function statics(...statics: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().staticProperties() + * @param statics Object(s) containing map of property names and values to mixin into each new stamp. + * @return A new Stamp. + */ + function staticProperties(...statics: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().deepStatics() + * @param deepStatics The object(s) containing static properties to be + * merged + * @returns A new stamp + */ + function deepStatics(...deepStatics: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().staticDeepProperties() + * @param deepStatics The object(s) containing static properties to be + * merged + * @returns A new stamp + */ + function staticDeepProperties(...deepStatics: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().conf() + * @param confs The object(s) containing metadata properties + * @returns A new Stamp + */ + function conf(...confs: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().configuration() + * @param confs The object(s) containing metadata properties + * @returns A new Stamp + */ + function configuration(...confs: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().deepConf() + * @param deepConfs The object(s) containing metadata properties + * @returns A new Stamp + */ + function deepConf(...deepConfs: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().deepConfiguration() + * @param deepConfs The object(s) containing metadata properties + * @returns A new Stamp + */ + function deepConfiguration(...deepConfs: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().propertyDescriptors() + * @param descriptors + * @returns A new Stamp + */ + function propertyDescriptors(...descriptors: Array<{}>): Stamp; + + /** + * A shortcut method for stampit().staticPropertyDescriptors() + * @param descriptors + * @returns A new Stamp + */ + function staticPropertyDescriptors(...descriptors: Array<{}>): Stamp; + + /** + * Take two or more Composables and combine them to produce a new Stamp. + * Combining overrides properties with last-in priority. + * @param composables Composable objects used to create the stamp. + * @return A new Stamp made of all the given composables. + */ + function compose(...composables: Composable[]): Stamp; +} + +export = stampit; diff --git a/types/stampit/v3/stampit-tests.ts b/types/stampit/v3/stampit-tests.ts new file mode 100644 index 0000000000..6f527dcaac --- /dev/null +++ b/types/stampit/v3/stampit-tests.ts @@ -0,0 +1,163 @@ +import stampit = require('stampit'); + +const a = stampit().init(function(options) { + const a = options.args[0]; + this.getA = () => { + return a; + }; +}); +a(); // Object -- so far so good. +a().getA(); // "a" + +const b = stampit().init(function() { + const a = 'b'; + this.getB = () => { + return a; + }; +}); + +const c = stampit.compose(a, b); +const foo = c(); // we won't throw this one away... +foo.getA(); // "a" +foo.getB(); // "b" + +// Here's a mixin with public methods, and some refs: +const membership = stampit({ + methods: { + members: {}, + add(member: any) { + this.members[member.name] = member; + return this; + }, + getMember(name: any) { + return this.members[name]; + } + }, + refs: { + members: {} + } +}); + +// Let's set some defaults: +const defaults = stampit().refs({ + name: 'The Saloon', + specials: 'Whisky, Gin, Tequila' +}); + +// Classical inheritance has nothing on this. No parent/child coupling. No deep inheritance hierarchies. +// Just good, clean code reusability. +const bar = stampit.compose(defaults, membership); +// Note that you can override refs on instantiation: +const myBar = bar({ name: 'Moe\'s' }); +// Silly, but proves that everything is as it should be. +myBar.add({ name: 'Homer' }).open().getMember('Homer'); + +const myStamp = stampit().methods({ + foo() { + return 'foo'; + }, + methodOverride() { + return false; + } +}).methods({ + bar() { + return 'bar'; + }, + methodOverride() { + return true; + } +}); + +myStamp.props({ + foo: { bar: 'bar' }, + refsOverride: false +}).refs({ + bar: 'bar', + refsOverride: true +}); + +myStamp.init(function() { + const secret = 'foo'; + + this.getSecret = () => { + return secret; + }; +}).init(function() { + this.a = true; +}).init(function() { + this.b = true; +}, function() { + this.c = true; +}); + +let obj = myStamp.create(); +obj.getSecret && obj.a && obj.b && obj.c; // true + +const newStamp = stampit({ refs: { defaultNum: 1 } }).compose(myStamp); + +const obj1 = stampit().methods({ + a() { + return 'a'; + } +}, { + b() { + return 'b'; + } +}).create(); + +const obj2 = stampit().refs({ + a: 'a' +}, { + b: 'b' +}).create(); + +obj = defaults.compose(newStamp, membership).create(); + +// The old constructor / class thing... +const Constructor = function Constructor() { + this.thing = 'initialized'; +}; +Constructor.prototype.foo = function foo() { + return 'foo'; +}; + +// A new stamp to compose with... +const newskool = stampit().methods({ + bar: function bar() { + return 'bar'; + } + // your methods here... +}).init(function() { + this.baz = 'baz'; +}); + +// Now you can compose those old constructors just like you could +// with any other stamp... +const myThing = stampit.compose(newskool); + +const t = myThing(); + +t.thing; // 'initialized', + +t.foo(); // 'foo', + +t.bar(); // 'bar' + +interface SomeStampInstance { + a: string; + b: string; +} + +// Test import of stamp type +interface SomeStamp extends stampit.Stamp { + (params: { a: number; b: boolean}): SomeStampInstance; +} + +const SomeStamp = stampit() + .init(function(params: { a: number; b: boolean}) { + this.a = '' + a; + this.b = '' + b; + }) as SomeStamp; + +SomeStamp({ a: 1, b: false }); // $ExpectType SomeStampInstance +SomeStamp({ a: 1, b: false }).a; // $ExpectType string diff --git a/types/stampit/v3/tsconfig.json b/types/stampit/v3/tsconfig.json new file mode 100644 index 0000000000..b152bdaaf0 --- /dev/null +++ b/types/stampit/v3/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": false, + "strictNullChecks": false, + "strictFunctionTypes": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "paths": { + "stampit": [ + "stampit/v3" + ] + } + }, + "files": [ + "index.d.ts", + "stampit-tests.ts" + ] +} diff --git a/types/stampit/v3/tslint.json b/types/stampit/v3/tslint.json new file mode 100644 index 0000000000..e60c15844f --- /dev/null +++ b/types/stampit/v3/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "dtslint/dt.json" +} \ No newline at end of file