Project setup (#1)
All checks were successful
continuous-integration/drone/push Build is passing

Sets up project build and CI

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: https://git.jvalver.de/Thilawyn/thilatrait/pulls/1
This commit was merged in pull request #1.
This commit is contained in:
Julien Valverdé
2023-12-09 02:22:38 +01:00
parent d7c80f0ba7
commit c6de7004d2
10 changed files with 360 additions and 0 deletions

68
src/class.ts Normal file
View File

@@ -0,0 +1,68 @@
import { AbstractClass } from "type-fest"
/**
* Copies all own properties and methods (excluding "length" and "prototype") of one class to another class.
*
* @param from The class whose properties and methods are to be copied.
* @param to The class to which the properties and methods are to be copied.
*/
export function copyClassProperties(
from: AbstractClass<any, any>,
to: AbstractClass<any, any>,
) {
Object.getOwnPropertyNames(from).forEach(name => {
if (name === "length"
|| name === "prototype"
)
return
Object.defineProperty(
to,
name,
Object.getOwnPropertyDescriptor(from, name) || Object.create(null),
)
})
Object.getOwnPropertyNames(from.prototype).forEach(name => {
Object.defineProperty(
to.prototype,
name,
Object.getOwnPropertyDescriptor(from.prototype, name) || Object.create(null),
)
})
}
export const flattenClass = <
C extends AbstractClass<any, any>
>(class_: C) =>
getInheritanceHierarchy(class_)
.reduce((flattened, current) => {
copyClassProperties(current, flattened)
return flattened
}, class {}) as C
/**
* Returns an array of classes representing the inheritance hierarchy of a given class constructor. The array includes the given class constructor itself and all its parent classes in the order of inheritance.
*
* @param class_ The class constructor for which to generate the inheritance hierarchy.
* @returns An array of class constructors representing the inheritance hierarchy of `Class`.
*/
export function getInheritanceHierarchy(
class_: AbstractClass<any, any>
): AbstractClass<any, any>[] {
const parent = Object.getPrototypeOf(class_)
return isClass(parent)
? [...getInheritanceHierarchy(parent), class_]
: [class_]
}
/**
* Determines whether a given value is a class constructor or not by checking if its `toString` method returns a string matching the pattern of a class definition.
*
* @param el The value to check for class constructor status.
* @returns A boolean indicating whether `el` is a class constructor or not.
*/
export const isClass = (el: { toString: () => string }) =>
Boolean(el.toString().match(/^class(?: [.\S]+)?(?: extends [.\S]+)? {[\s\S]*}$/))

3
src/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./class"
export * from "./trait"
export * from "./utils"

29
src/tests.ts Normal file
View File

@@ -0,0 +1,29 @@
import { mixTraits } from "./trait"
abstract class Identified<ID> {
abstract id: ID
equals(el: Identified<ID>) {
return this.id === el.id
}
}
abstract class ProvideIdentified<ID> extends Identified<ID> {
id!: ID
}
abstract class Permissible {
protected permissions: string[] = []
}
class User extends mixTraits(
Identified<bigint>,
// Identified<string>,
Permissible,
) {
id: bigint = BigInt(-1)
}
const user = new User()

87
src/trait.ts Normal file
View File

@@ -0,0 +1,87 @@
import { AbstractClass, Class, UnionToIntersection } from "type-fest"
import { copyClassProperties, flattenClass } from "./class"
import { StaticMembers } from "./utils"
export type Trait<T> =
AbstractClass<T, []>
// export function applyTrait<
// C extends Class<any, any> | AbstractClass<any, any>,
// TraitC extends Trait<any>,
// >(
// class_: C,
// 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) => {
copyClassProperties(flattenClass(trait), class_)
return class_
}, class extends classToExtend {}) as (
AbstractClass<
InstanceType<C> &
UnionToIntersection<
InstanceType<Traits[number]>
>,
ConstructorParameters<C>
> &
StaticMembers<
C &
UnionToIntersection<
Traits[number]
>
>
)
export const mixTraits = <
Traits extends readonly Trait<any>[]
>(
...traits: Traits
) =>
traits.reduce((class_, trait) => {
copyClassProperties(flattenClass(trait), class_)
return class_
}, class {}) as (
Trait<
UnionToIntersection<
InstanceType<
Traits[number]
>
>
> &
StaticMembers<
UnionToIntersection<
Traits[number]
>
>
)

1
src/utils.ts Normal file
View File

@@ -0,0 +1 @@
export type StaticMembers<C> = Pick<C, keyof C>