118 lines
2.9 KiB
TypeScript
118 lines
2.9 KiB
TypeScript
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<any>
|
|
> = Opaque<
|
|
TraitApplier<C>,
|
|
"@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<any>
|
|
> = (
|
|
(Super: Opaque<AbstractConstructor<object>, TraitApplierSuperTag>) => Opaque<C, TraitApplierSuperTag>
|
|
)
|
|
|
|
/**
|
|
* 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 = <ID>() =>
|
|
* 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 = <ID>(defaultID: ID) =>
|
|
* trait(Super => {
|
|
* abstract class ImplementsIdentifiable extends extendsAndExpresses(
|
|
* Super,
|
|
* Identifiable<ID>(),
|
|
* ) {
|
|
* id: ID = defaultID
|
|
*
|
|
* // Optional
|
|
* constructor(...args: any[]) {
|
|
* super(...args)
|
|
* }
|
|
* }
|
|
*
|
|
* return ImplementsIdentifiable
|
|
* })
|
|
* ```
|
|
*/
|
|
export function trait<
|
|
C extends AbstractClass<any>
|
|
>(
|
|
applier: TraitApplier<C>
|
|
) {
|
|
return applier as Trait<C>
|
|
}
|
|
|
|
/**
|
|
* Returns the class type of a trait.
|
|
* @template T - The trait type.
|
|
*/
|
|
export type TraitClass<T> = (
|
|
T extends Trait<infer C>
|
|
? C
|
|
: never
|
|
)
|
|
|
|
/**
|
|
* Returns the instance type of a trait.
|
|
* @template T - The trait type.
|
|
*/
|
|
export type TraitInstance<T> = (
|
|
T extends Trait<infer C>
|
|
? InstanceType<C>
|
|
: never
|
|
)
|