diff --git a/src/trait.ts b/src/trait.ts index 21fd00c..a6d647a 100644 --- a/src/trait.ts +++ b/src/trait.ts @@ -1,117 +1,56 @@ -import { AbstractClass, AbstractConstructor, Opaque } from "type-fest" +import { AbstractClass, Class, Opaque } from "type-fest" +import { StaticMembers } from "./util" -/** - * Represents a trait that can be applied to a class. - * @template C - The abstract class type. - */ -export type Trait< - C extends AbstractClass -> = Opaque< - TraitApplier, - "@thilawyn/thilatrait/Trait" -> - -export type TraitApplierSuperTag = "@thilawyn/thilatrait/Super" - -/** - * Represents the function signature for applying a trait to a parent class. - * @template C - The abstract class type. - */ -export type TraitApplier< - C extends AbstractClass +type RemoveAbstractMembersFromImpl< + ImplWithAbstractMembers extends Class, + AbstractMembers extends object, > = ( - (Super: Opaque, TraitApplierSuperTag>) => Opaque + Class< + Omit, keyof AbstractMembers>, + ConstructorParameters + > & + StaticMembers ) -/** - * Creates a trait using the provided trait applier function. - * @template C - The abstract class type. - * @param applier - The trait applier function. - * @returns A trait. - * @example - * Creates a trait: - * ```ts - * const Permissible = trait(Super => { - * abstract class Permissible extends Super { - * static readonly defaultPermissions: string[] = [] - * permissions: string[] = [] - * - * // Constructor is optional - * // If you wish to use it, make sure it takes any[] as an args array and passes it to the super call. This is necessary for inheritance to work properly. - * // Trait constructors cannot have typed arguments of their own, they only serve to run logic during object instantiation. - * constructor(...args: any[]) { - * super(...args) - * } - * } - * - * return Permissible - * }) - * ``` - * Creates a generic trait: - * ```ts - * const Identifiable = () => - * trait(Super => { - * abstract class Identifiable extends Super { - * abstract readonly id: ID - * - * equals(el: Identifiable) { - * return this.id === el.id - * } - * - * // Optional - * constructor(...args: any[]) { - * super(...args) - * } - * } - * - * return Identifiable - * }) - * ``` - * Creates a subtrait: - * ```ts - * const ImplementsIdentifiable = (defaultID: ID) => - * trait(Super => { - * abstract class ImplementsIdentifiable extends extendsAndExpresses( - * Super, - * Identifiable(), - * ) { - * id: ID = defaultID - * - * // Optional - * constructor(...args: any[]) { - * super(...args) - * } - * } - * - * return ImplementsIdentifiable - * }) - * ``` - */ + +export type TraitTag = "@thilawyn/traitify-ts/Trait" + +export type Trait< + AbstractMembers extends object, + Impl extends Class, +> = ( + Opaque<{ + readonly AbstractMembers: AbstractMembers + readonly Impl: Impl + }, TraitTag> +) + + +export type TraitApplierSuperTag = "@thilawyn/traitify-ts/Super" + +export type TraitApplier< + AbstractMembers extends object, + ImplWithAbstractMembers extends Class, +> = ( + (Super: Opaque, TraitApplierSuperTag>) => ( + Opaque + ) +) + + export function trait< - C extends AbstractClass ->( - applier: TraitApplier + AbstractMembers extends object = {} +>(): ( + >( + applier: TraitApplier + ) => Trait< + AbstractMembers, + RemoveAbstractMembersFromImpl< + ImplWithAbstractMembers, + AbstractMembers + > + > ) { - return applier as Trait + return (applier) => applier() } - -/** - * Returns the class type of a trait. - * @template T - The trait type. - */ -export type TraitClass = ( - T extends Trait - ? C - : never -) - -/** - * Returns the instance type of a trait. - * @template T - The trait type. - */ -export type TraitInstance = ( - T extends Trait - ? InstanceType - : never -)