makeLinkClass
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Julien Valverdé
2023-12-25 01:46:38 +01:00
parent d8e509f540
commit a32c9c70f5
2 changed files with 108 additions and 69 deletions

View File

@@ -27,6 +27,10 @@ abstract class Identified<ID> {
equals(el: Identified<ID>) { equals(el: Identified<ID>) {
return this.id === el.id return this.id === el.id
} }
// initializer() {
// console.log("Identified initializer")
// }
} }
class ImplementsIdentifiable<ID> extends Identified<ID> { class ImplementsIdentifiable<ID> extends Identified<ID> {
@@ -36,7 +40,13 @@ class ImplementsIdentifiable<ID> extends Identified<ID> {
abstract class Permissible { abstract class Permissible {
static readonly defaultPermissions: string[] = [] static readonly defaultPermissions: string[] = []
permissions: string[] = [] // permissions: string[] = []
permissions!: string[]
initializer() {
console.log("Permissible initializer")
this.permissions = []
}
} }
@@ -55,11 +65,11 @@ class User extends expresses(
console.log(new User(BigInt(1))) console.log(new User(BigInt(1)))
// console.log(Permissible.constructor()) console.log(Permissible.constructor())
// console.log(Object.getOwnPropertyNames(User.prototype)) console.log(Object.getOwnPropertyNames(User.prototype))
// const user1 = new User(BigInt(1)) const user1 = new User(BigInt(1))
// const user2 = new User(BigInt(2)) const user2 = new User(BigInt(2))
// console.log(user1.equals(user2)) console.log(user1.equals(user2))
// console.log(user1.permissions) console.log(user1.permissions)

View File

@@ -1,72 +1,85 @@
import { AbstractClass, Class, UnionToIntersection } from "type-fest" import { AbstractClass, Class, UnionToIntersection } from "type-fest"
import { StaticMembers, copyProperties, flattenClass, getInheritanceHierarchy } from "./util/class" 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> = export type Trait<T> =
AbstractClass<T, []> AbstractClass<T, []>
// export function applyTrait< /**
// C extends Class<any, any> | AbstractClass<any, any>, * Creates a link class that expresses the given traits.
// TraitC extends Trait<any>, * @param traits - An array of traits to be expressed by the link class.
// >( * @returns A dynamically created class that expresses the given traits.
// class_: C, * @typeParam Traits - An array of traits that the link class expresses.
// trait: TraitC, */
// ) {
// copyClassProperties(trait, class_)
// return class_ as (
// (C extends Class<any, any>
// ? Class<
// InstanceType<C> & InstanceType<TraitC>,
// ConstructorParameters<C>
// >
// : AbstractClass<
// InstanceType<C> & InstanceType<TraitC>,
// ConstructorParameters<C>
// >
// ) &
// StaticMembers<C> &
// StaticMembers<TraitC>
// )
// }
export const extendAndApplyTraits = <
C extends Class<any, any> | AbstractClass<any, any>,
Traits extends readonly Trait<any>[],
>(
classToExtend: C,
traits: Traits,
) =>
traits.reduce((class_, trait) => {
copyProperties(flattenClass(trait), class_)
return class_
}, class extends classToExtend {}) as (
AbstractClass<
InstanceType<C> &
UnionToIntersection<
InstanceType<Traits[number]>
>,
ConstructorParameters<C>
> &
StaticMembers<
C &
UnionToIntersection<
Traits[number]
>
>
)
export function expresses< export function expresses<
Traits extends readonly Trait<any>[] Traits extends readonly Trait<any>[]
>( >(
...traits: Traits ...traits: Traits
) { ) {
const class_ = class {} 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 => { traits.forEach(trait => {
getInheritanceHierarchy(trait).forEach(current => { getInheritanceHierarchy(trait).forEach(current => {
@@ -80,13 +93,29 @@ export function expresses<
}) })
return class_ as unknown as ( return class_ as unknown as (
Trait< (C extends Class<any, any> | AbstractClass<any, any>
? (
AbstractClass<
InstanceType<C> &
UnionToIntersection<
InstanceType<
Traits[number]
>
>,
ConstructorParameters<C>
> &
StaticMembers<C>
)
: Trait<
UnionToIntersection< UnionToIntersection<
InstanceType< InstanceType<
Traits[number] Traits[number]
> >
> >
> & >
) &
StaticMembers< StaticMembers<
UnionToIntersection< UnionToIntersection<