diff --git a/src/tests.ts b/src/tests.ts index a959718..5cbafb6 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -27,6 +27,10 @@ abstract class Identified { equals(el: Identified) { return this.id === el.id } + + // initializer() { + // console.log("Identified initializer") + // } } class ImplementsIdentifiable extends Identified { @@ -36,7 +40,13 @@ class ImplementsIdentifiable extends Identified { abstract class Permissible { static readonly defaultPermissions: string[] = [] - permissions: string[] = [] + // permissions: string[] = [] + permissions!: string[] + + initializer() { + console.log("Permissible initializer") + this.permissions = [] + } } @@ -55,11 +65,11 @@ class User extends expresses( console.log(new User(BigInt(1))) -// console.log(Permissible.constructor()) -// console.log(Object.getOwnPropertyNames(User.prototype)) +console.log(Permissible.constructor()) +console.log(Object.getOwnPropertyNames(User.prototype)) -// const user1 = new User(BigInt(1)) -// const user2 = new User(BigInt(2)) +const user1 = new User(BigInt(1)) +const user2 = new User(BigInt(2)) -// console.log(user1.equals(user2)) -// console.log(user1.permissions) +console.log(user1.equals(user2)) +console.log(user1.permissions) diff --git a/src/trait.ts b/src/trait.ts index 2b4b178..10cc87b 100644 --- a/src/trait.ts +++ b/src/trait.ts @@ -1,72 +1,85 @@ import { AbstractClass, Class, UnionToIntersection } from "type-fest" -import { StaticMembers, copyProperties, flattenClass, getInheritanceHierarchy } from "./util/class" +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 -// export function applyTrait< -// C extends Class | AbstractClass, -// TraitC extends Trait, -// >( -// class_: C, -// trait: TraitC, -// ) { -// copyClassProperties(trait, class_) - -// return class_ as ( -// (C extends Class -// ? Class< -// InstanceType & InstanceType, -// ConstructorParameters -// > -// : AbstractClass< -// InstanceType & InstanceType, -// ConstructorParameters -// > -// ) & -// StaticMembers & -// StaticMembers -// ) -// } - - -export const extendAndApplyTraits = < - C extends Class | AbstractClass, - Traits extends readonly Trait[], ->( - classToExtend: C, - traits: Traits, -) => - traits.reduce((class_, trait) => { - copyProperties(flattenClass(trait), class_) - return class_ - }, class extends classToExtend {}) as ( - AbstractClass< - InstanceType & - UnionToIntersection< - InstanceType - >, - - ConstructorParameters - > & - - StaticMembers< - C & - UnionToIntersection< - Traits[number] - > - > - ) - - +/** + * 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 ) { - const class_ = class {} + 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 => { @@ -80,13 +93,29 @@ export function expresses< }) return class_ as unknown as ( - Trait< - UnionToIntersection< - InstanceType< - Traits[number] + (C extends Class | AbstractClass + ? ( + AbstractClass< + InstanceType & + UnionToIntersection< + InstanceType< + Traits[number] + > + >, + + ConstructorParameters + > & + + StaticMembers + ) + : Trait< + UnionToIntersection< + InstanceType< + Traits[number] + > > > - > & + ) & StaticMembers< UnionToIntersection<