import { AbstractClass, Class, UnionToIntersection } from "type-fest" import { StaticMembers, copyProperties, getInheritanceHierarchy } from "./util/class" /** * Represents a trait that can be used to define common behavior * for classes and abstract classes. * @typeParam T - The type of the trait. */ export type Trait = AbstractClass /** * Creates a link class that expresses the given traits. * @param traits - An array of traits to be expressed by the link class. * @returns A dynamically created class that expresses the given traits. * @typeParam Traits - An array of traits that the link class expresses. */ export function expresses< Traits extends readonly Trait[] >( ...traits: Traits ) { return makeLinkClass(traits) } /** * Creates a link class that extends a base class and expresses the given traits. * @param extend - The base class or abstract class to extend. * @param traits - An array of traits to be expressed by the link class. * @returns A dynamically created class that extends the given base class and expresses the given traits. * @typeParam C - The type of the base class to extend. * @typeParam Traits - An array of traits that the link class expresses. */ export function extendsAndExpresses< C extends Class | AbstractClass, Traits extends readonly Trait[], >( extend: C, ...traits: Traits ) { return makeLinkClass(traits, extend) } /** * Creates a link class that expresses the given traits and optionally extends a base class. * @param traits - An array of traits to be expressed by the link class. * @param extend - The base class or abstract class to extend (optional). * @returns A dynamically created class that expresses the given traits and extends the base class. * @typeParam Traits - An array of traits that the link class expresses. * @typeParam C - The type of the base class to extend (optional). */ export function makeLinkClass< Traits extends readonly Trait[], C extends Class | AbstractClass | undefined = undefined, >( traits: Traits, extend?: C, ) { const class_ = extend ? class extends extend { constructor(...args: any[]) { super(...args) traits.forEach(trait => { trait.prototype.initializer?.call(this) }) } } : class { constructor() { traits.forEach(trait => { trait.prototype.initializer?.call(this) }) } } traits.forEach(trait => { getInheritanceHierarchy(trait).forEach(current => { copyProperties( current, class_, ["name", "length"], ["constructor"], ) }) }) return class_ as unknown as ( (C extends Class | AbstractClass ? ( AbstractClass< InstanceType & UnionToIntersection< InstanceType< Traits[number] > >, ConstructorParameters > & StaticMembers ) : Trait< UnionToIntersection< InstanceType< Traits[number] > > > ) & StaticMembers< UnionToIntersection< Traits[number] > > ) }