import { AbstractClass, AbstractConstructor, Opaque } from "type-fest" /** * 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 > = ( (Super: Opaque, TraitApplierSuperTag>) => Opaque ) /** * 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 function trait< C extends AbstractClass >( applier: TraitApplier ) { return applier as Trait } /** * 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 )