import { TupleConcat, TupleToIntersection } from './types'; import { SetInstanceOf } from './instanceof'; export interface Constructor extends Function { new(...args: Args): Instance; } export interface AbstractClass extends Function { prototype: Instance; } export type ClassType = AbstractClass | Constructor; export type TFactory = (superClass: TBase) => TBase; // exclude the constructor from T export type ExcludeConstructor = { [P in keyof T]: T[P] extends new(...args: any[]) => any ? never : T[P]; }; // removes all constructors of a tuple export type ExcludeConstructors = { // [P in Extract]: ExcludeConstructor; [P in keyof T]: ExcludeConstructor; }; // converts a tuple of constructor types (ex: [Constructor, Constructor]) to a tuple of instances types export type InstancesTypes any)[]> = { [P in keyof T]: T[P] extends new (...args: any[]) => infer R ? R : never; // [P in Extract]: T[P] extends new (...args: any[]) => infer R ? R : never; }; // converts a tuple of constructor types (ex: [Constructor, Constructor]) to a tuple of their parameters export type ConstructorsParameters any)[]> = { [P in keyof T]: T[P] extends new (...args: infer P) => any ? P : never; }; // returns a tuple where types are the expected factories types export type TMakeFactoryFactories any)[]> = { [P in keyof TSuperClasses]: TSuperClasses[P] extends (new (...args: any[]) => infer R) ? (superClass: any) => new(/*ownArgs: any[], */...args: any[]) => R : never; }; export type TMakeFactorySuperInstance = TupleToIntersection>; export type TMakeFactoryInstance = InstanceType & TMakeFactorySuperInstance & InstanceType; export type TMakeFactorySuperStatic = TupleToIntersection>; export type TMakeFactoryStatic = ExcludeConstructor & TMakeFactorySuperStatic & ExcludeConstructor; export type TMakeFactoryCreateSuperClass = TSuperClasses extends [] ? new(...args: any) => any : TMakeFactorySuperStatic & { new(...args: any): TMakeFactorySuperInstance; }; export type TMakeFactoryClass = TMakeFactoryStatic & { new(ownArgs: ConstructorParameters, ...args: TupleConcat, ConstructorParameters>): TMakeFactoryInstance; }; export interface IMakeFactoryOptions { name?: string; // force a name for this class instanceOf?: ClassType; // force instanceof for this class waterMarks?: symbol[]; // uniq symbol to identify the class type } /** * Creates a new class from : * - a base class * - some other factories * - a child class * => child extends factories extends base * @param create * @param factories * @param superClass * @param options */ export function MakeFactory( create: (superClass: TMakeFactoryCreateSuperClass) => TMakeFactoryCreateSuperClass, factories: TMakeFactoryFactories, superClass: TBase, options: IMakeFactoryOptions = {} ): TMakeFactoryClass { let _superClass: any = superClass; for (let i = factories.length - 1; i >= 0; i--) { _superClass = factories[i](_superClass); } const _class: TMakeFactoryClass = create(_superClass) as unknown as TMakeFactoryClass; Object.defineProperty(_class, IS_FACTORY_CLASS, { value: true, }); if (typeof options.name === 'string') { Object.defineProperty(_class, 'name', { configurable: true, enumerable: false, value: options.name, writable: false, }); } if (Array.isArray(options.waterMarks)) { for (let i = 0, l = options.waterMarks.length; i < l; i++) { Object.defineProperty(_class, options.waterMarks[i], { value: true, }); } } if (options.instanceOf !== void 0) { SetInstanceOf(options.instanceOf, _class); } return _class; } /** * Returns true if class has the specified watermark * @param _class * @param waterMark * @param direct */ export function HasFactoryWaterMark(_class: (new(...args: any[]) => any), waterMark: symbol, direct: boolean = true): boolean { return (_class[waterMark] === true) && (direct ? _class.hasOwnProperty(waterMark) : true); } const IS_FACTORY_CLASS = Symbol('is-factory-class'); /** * Returns true if class has been build with MakeFactory * @param _class * @param direct */ export function IsFactoryClass(_class: (new(...args: any[]) => any), direct: boolean = true): boolean { return (_class[IS_FACTORY_CLASS] === true) && (direct ? _class.hasOwnProperty(IS_FACTORY_CLASS) : true); } /** * Replace incoming args for the super class by superArgs in the context of a Factory class * @param args - list of remaining args from the constructor that should be passed to 'super' * @param superArgs - list of overloaded args to pass to the child */ export function SetSuperArgsForFactoryClass(args: any[], superArgs: any[]): any[] { if (Array.isArray(args[0])) { args[0] = superArgs; return args; } else { throw new TypeError(`Expected array as argument 0`); } } /** * Same as previous but class is a standard class instead * @param args * @param superArgs */ export function SetSuperArgsForStandardClass(args: any[], superArgs: any[]): any[] { for (let i = 0, l = superArgs.length; i < l; i++) { args[i] = superArgs[i]; } return args; } export type TSetSuperArgs = (args: any[], superArgs: any[]) => any[]; export function GetSetSuperArgsFunction(isFactoryClass: boolean): TSetSuperArgs { return isFactoryClass ? SetSuperArgsForFactoryClass : SetSuperArgsForStandardClass; } export interface IBaseClass { } export interface IBaseClassConstructor { new(): IBaseClass; } export class BaseClass { }