127 lines
3.7 KiB
TypeScript
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]
|
|
>
|
|
>
|
|
)
|
|
}
|