This commit is contained in:
117
src/trait.ts
Normal file
117
src/trait.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
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
|
||||
)
|
||||
Reference in New Issue
Block a user