Files
thilatrait/src/trait.ts
Julien Valverdé a32c9c70f5
All checks were successful
continuous-integration/drone/push Build is passing
makeLinkClass
2023-12-25 01:46:38 +01:00

127 lines
3.7 KiB
TypeScript

import { AbstractClass, Class, UnionToIntersection } from "type-fest"
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<T> =
AbstractClass<T, []>
/**
* 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<any>[]
>(
...traits: Traits
) {
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<any, any>
| AbstractClass<any, any>,
Traits extends readonly Trait<any>[],
>(
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<any>[],
C extends Class<any, any>
| AbstractClass<any, any>
| 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 => {
copyProperties(
current,
class_,
["name", "length"],
["constructor"],
)
})
})
return class_ as unknown as (
(C extends Class<any, any> | AbstractClass<any, any>
? (
AbstractClass<
InstanceType<C> &
UnionToIntersection<
InstanceType<
Traits[number]
>
>,
ConstructorParameters<C>
> &
StaticMembers<C>
)
: Trait<
UnionToIntersection<
InstanceType<
Traits[number]
>
>
>
) &
StaticMembers<
UnionToIntersection<
Traits[number]
>
>
)
}