From e814fa89166dc1ed10db157b137f4e4ad6d581fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 9 Dec 2023 02:50:24 +0100 Subject: [PATCH 01/21] Class util refactoring --- src/class.ts | 68 ----------------------------------- src/index.ts | 2 -- src/trait.ts | 3 +- src/util/class.ts | 90 +++++++++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 1 - 5 files changed, 91 insertions(+), 73 deletions(-) delete mode 100644 src/class.ts create mode 100644 src/util/class.ts delete mode 100644 src/utils.ts diff --git a/src/class.ts b/src/class.ts deleted file mode 100644 index 8cff9e9..0000000 --- a/src/class.ts +++ /dev/null @@ -1,68 +0,0 @@ -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, - to: AbstractClass, -) { - 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 ->(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 -): AbstractClass[] { - 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]*}$/)) diff --git a/src/index.ts b/src/index.ts index 46c8360..d46d244 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1 @@ -export * from "./class" export * from "./trait" -export * from "./utils" diff --git a/src/trait.ts b/src/trait.ts index 021d69a..22f199d 100644 --- a/src/trait.ts +++ b/src/trait.ts @@ -1,6 +1,5 @@ import { AbstractClass, Class, UnionToIntersection } from "type-fest" -import { copyClassProperties, flattenClass } from "./class" -import { StaticMembers } from "./utils" +import { StaticMembers, copyClassProperties, flattenClass } from "./util/class" export type Trait = diff --git a/src/util/class.ts b/src/util/class.ts new file mode 100644 index 0000000..671ff45 --- /dev/null +++ b/src/util/class.ts @@ -0,0 +1,90 @@ +import { AbstractClass } from "type-fest" + + +/** + * Represents the static members of a class. + * + * @template C - The type of the class for which static members are extracted. + * @typeparam The static members of the class. + */ +export type StaticMembers = Pick + + +/** + * Copies properties from one class to another, including static and prototype properties. + * + * @param from - The source class to copy properties from. + * @param to - The destination class to copy properties to. + */ +export function copyClassProperties( + from: AbstractClass, + to: AbstractClass, +) { + 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), + ) + }) +} + + +/** + * Flattens the inheritance hierarchy of a given class by copying all properties + * from its superclass chain into a single object. + * + * @template C - The type of the class to be flattened, extending AbstractClass. + * @param C - The class to be flattened. + * @returns A new class with properties flattened from the entire inheritance hierarchy. + */ +export function flattenClass< + C extends AbstractClass +>(class_: C) { + return getInheritanceHierarchy(class_) + .reduce((flattened, current) => { + copyClassProperties(current, flattened) + return flattened + }, class {}) as C +} + + +/** + * Retrieves the inheritance hierarchy of a given class, including itself. + * + * @param class_ - The class for which the inheritance hierarchy is retrieved. + * @returns An array representing the inheritance hierarchy. + */ +export function getInheritanceHierarchy( + class_: AbstractClass +): AbstractClass[] { + const parent = Object.getPrototypeOf(class_) + + return isClass(parent) + ? [...getInheritanceHierarchy(parent), class_] + : [class_] +} + + +/** + * Checks if a given element appears to be a class based on its string representation. + * + * @param el - The element to check for being a class. + * @returns `true` if the element is likely a class; otherwise, `false`. + */ +export function isClass(el: { toString: () => string }) { + return Boolean(el.toString().match(/^class(?: [.\S]+)?(?: extends [.\S]+)? {[\s\S]*}$/)) +} diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 87f5f52..0000000 --- a/src/utils.ts +++ /dev/null @@ -1 +0,0 @@ -export type StaticMembers = Pick -- 2.49.1 From 04da4b0f59018d71e14f4344e7dbd5391ea1ee2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 9 Dec 2023 03:03:09 +0100 Subject: [PATCH 02/21] Tests --- src/tests.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/tests.ts b/src/tests.ts index 4520fd6..a27d87d 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -9,12 +9,8 @@ abstract class Identified { } } -abstract class ProvideIdentified extends Identified { - id!: ID -} - abstract class Permissible { - protected permissions: string[] = [] + permissions: string[] = [] } @@ -23,7 +19,16 @@ class User extends mixTraits( // Identified, Permissible, ) { - id: bigint = BigInt(-1) + readonly id: bigint + + constructor(id: bigint) { + super() + this.id = id + } } -const user = new User() +const user1 = new User(BigInt(1)) +const user2 = new User(BigInt(2)) + +console.log(user1.equals(user2)) +console.log(user1.permissions) -- 2.49.1 From 706fe3688d2c591ec9ad12067b45e23a1f42c87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 9 Dec 2023 03:31:46 +0100 Subject: [PATCH 03/21] Tests --- src/tests.ts | 7 +++++-- src/util/class.ts | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/tests.ts b/src/tests.ts index a27d87d..0e88cba 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -27,8 +27,11 @@ class User extends mixTraits( } } +console.log(Permissible.constructor()) +// console.log(Object.getOwnPropertyNames(User.prototype)) + const user1 = new User(BigInt(1)) const user2 = new User(BigInt(2)) -console.log(user1.equals(user2)) -console.log(user1.permissions) +// console.log(user1.equals(user2)) +// console.log(user1.permissions) diff --git a/src/util/class.ts b/src/util/class.ts index 671ff45..aecd83d 100644 --- a/src/util/class.ts +++ b/src/util/class.ts @@ -21,7 +21,10 @@ export function copyClassProperties( to: AbstractClass, ) { Object.getOwnPropertyNames(from).forEach(name => { - if (name === "length" + // console.log(from, to, name, Object.getOwnPropertyDescriptor(from, name)) + + if (name === "name" + || name === "length" || name === "prototype" ) return @@ -34,6 +37,11 @@ export function copyClassProperties( }) Object.getOwnPropertyNames(from.prototype).forEach(name => { + // console.log(from, to, name, Object.getOwnPropertyDescriptor(from, name)) + + if (name === "constructor") + return + Object.defineProperty( to.prototype, name, -- 2.49.1 From fcfed14306d83cf978b9d3e3237a4b47957f6853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 9 Dec 2023 03:39:58 +0100 Subject: [PATCH 04/21] Rollup cleanup plugin --- bun.lockb | Bin 20853 -> 23850 bytes package.json | 1 + rollup.config.js | 12 ++++++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index ada479ec1da18794383ac5e5eddefbc73b9585e2..311609432feafeb05e05354b760c648e5d47902c 100755 GIT binary patch delta 4674 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 delta 2861 zcmd5;dr*{B6u)=dRlY?YA`#oBpiKBcKY?Xg5S9-RVb(OoOdC@J1Px|IV~HVOtQFeU zXzH1%)Kn~ODsxQnHRc~#T5VtoGd-pk4e3l-K4#KJN3nkA+ud-otZBxX?##KrbMATE z``vTycki2rB&NG z8EdB}G@Y^qgg|c=!aEAA0!#w3JOCICtXtrAKfX|i&O3w{2zfv79^m?*>;+mNmjM%j zg}_0;MBosh1SSBx4FNm~9Mpt??ZE*bkOS5Nt-!gSs)nlCdQZK_U0s1iG)N&LFg`Vi zgjI$^u7TVS=)h{(uN*ReKo?}zyC5^#fN{Vi7_+^nYJNjgRhXHL1hyt1kYGA}R{7z_LvL9lz?E+CH|2gU&R z0po!KBsPW(1p>3-#{PkYtOl~fa>mvN4X;YdGESD6n`jcEOy_ojo2he3A0aArZa%o#I@k6GH@t7it{GfF z`#re8964b^Ok)paEx0T!*gzddhxC)8Xojm{G+xP*rQ}jHd4T*F`c)ybIZsX@mswM` zW9!D~KBbC7o*}z( zh>|bS{W>sX4iZre$wZ~$4*5R0qBTQLe|k1LPio|cuqcMi1I9FATLlxh8D@4WdrVG@i z&~)p6h5G!rbl|{ag8}~zS^eMg5SagVBt~EIjS2tDJn+~3FA|ZbulH7&yJaF;uf%QZ zPqR|C$duNel)eVjLLAr#k|XDl*J!*m^c$Vyu2yS2fm$qW^{LZkt2gziVjh;EPcL#a z=(C*hkv2$=28S~2RO=it{Lq=}B_4z1vo=7S1AT$kA(J_|V@uIEu86*L-@ z4&sm;(GKF%kOJaUz%}axam{nRm$3nwJCmn1v751Vy|b@0w*-$zt&={^OXfPK^Lg1+ zsKwcnAm9Lbx7_O;nbKTU5hiEbRhtTDUIyPYM`Cg=Ju+ckn?cUbP_tMgXq9HM;y%!? zg9rA9#Z1{Xeh>6%YO0!+Er!w&%_42|6|6$n*?~8be^}M=Ub`M8jiXGZsC-1 zV`ZMxp*CV|JE<$*l6egJ1n4L3{qVK#BG%OO>Q6z#pVd6`qq5}_%x74WrhA^1q!x^@ zUV*_~F!=M%M9XOM6b!b8F8p_5<6>q#zy3+1%upS?VBw=(1r~egfl#{P+`0Ugj_bX8 z{SAT0ed;?a-Jkqo=+$&nPl3f6dUIr5SUf2*dSyqiK9SN2EwYgAE3{b4kiSGkaSYv= zm45YPaj%t^nhGuU&=$4B zMAoqu>&^Pj(DPf$L6gQ>?4gIu+R-ysCGT@)b6sG6R9j}~CECk;wT9X~ zPMg>>`107?aClU8r>PElhid1o4ZTgiO?r3Q<-8vWnpAAMEi+JSv7tAQZQYzjM>e%A zHOSw>DZVHX=PtX*hKqWBkwxyNl|?rD&8A7X=lJ~+dI*zhB{ns~Ku$YFZJU zhkq$4<81OUImcP7p})d{mrg`2duGCm(eeXC;ksH*kB#FheGdIb+CDBNW*Z8XYw;i- yR Date: Sat, 9 Dec 2023 03:40:30 +0100 Subject: [PATCH 05/21] Fixed lockfile --- bun.lockb | Bin 23850 -> 23850 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 311609432feafeb05e05354b760c648e5d47902c..c6435276b23befef2cf5aff26ea03b90ecf1c67a 100755 GIT binary patch delta 22 ecmZ3ri*eO1#to+S?2K{7dPaJNn{Djh$pQdef(L*A delta 22 acmZ3ri*eO1#to+S>`V+`u-V4`oh$%Q@CGpe -- 2.49.1 From d8e509f5403b2670fede001266c553996c7ee29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 13 Dec 2023 19:55:14 +0100 Subject: [PATCH 06/21] Refactoring --- src/tests.ts | 38 ++++++++++++++--- src/trait.ts | 27 ++++++++---- src/util/class.ts | 105 +++++++++++++++++++++++++--------------------- 3 files changed, 110 insertions(+), 60 deletions(-) diff --git a/src/tests.ts b/src/tests.ts index 0e88cba..a959718 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1,4 +1,24 @@ -import { mixTraits } from "./trait" +import { AbstractClass } from "type-fest" +import { expresses } from "./trait" + + +function inspectClass(class_: AbstractClass) { + Object.getOwnPropertyNames(class_).forEach(name => { + console.log( + "[static]", + name, + Object.getOwnPropertyDescriptor(class_, name) + ) + }) + + Object.getOwnPropertyNames(class_.prototype).forEach(name => { + console.log( + "[prototype]", + name, + Object.getOwnPropertyDescriptor(class_.prototype, name) + ) + }) +} abstract class Identified { @@ -9,12 +29,18 @@ abstract class Identified { } } +class ImplementsIdentifiable extends Identified { + id!: ID +} + + abstract class Permissible { + static readonly defaultPermissions: string[] = [] permissions: string[] = [] } -class User extends mixTraits( +class User extends expresses( Identified, // Identified, Permissible, @@ -27,11 +53,13 @@ class User extends mixTraits( } } -console.log(Permissible.constructor()) +console.log(new User(BigInt(1))) + +// console.log(Permissible.constructor()) // console.log(Object.getOwnPropertyNames(User.prototype)) -const user1 = new User(BigInt(1)) -const user2 = new User(BigInt(2)) +// const user1 = new User(BigInt(1)) +// const user2 = new User(BigInt(2)) // console.log(user1.equals(user2)) // console.log(user1.permissions) diff --git a/src/trait.ts b/src/trait.ts index 22f199d..2b4b178 100644 --- a/src/trait.ts +++ b/src/trait.ts @@ -1,5 +1,5 @@ import { AbstractClass, Class, UnionToIntersection } from "type-fest" -import { StaticMembers, copyClassProperties, flattenClass } from "./util/class" +import { StaticMembers, copyProperties, flattenClass, getInheritanceHierarchy } from "./util/class" export type Trait = @@ -40,7 +40,7 @@ export const extendAndApplyTraits = < traits: Traits, ) => traits.reduce((class_, trait) => { - copyClassProperties(flattenClass(trait), class_) + copyProperties(flattenClass(trait), class_) return class_ }, class extends classToExtend {}) as ( AbstractClass< @@ -61,15 +61,25 @@ export const extendAndApplyTraits = < ) -export const mixTraits = < +export function expresses< Traits extends readonly Trait[] >( ...traits: Traits -) => - traits.reduce((class_, trait) => { - copyClassProperties(flattenClass(trait), class_) - return class_ - }, class {}) as ( +) { + const class_ = class {} + + traits.forEach(trait => { + getInheritanceHierarchy(trait).forEach(current => { + copyProperties( + current, + class_, + ["name", "length"], + ["constructor"], + ) + }) + }) + + return class_ as unknown as ( Trait< UnionToIntersection< InstanceType< @@ -84,3 +94,4 @@ export const mixTraits = < > > ) +} diff --git a/src/util/class.ts b/src/util/class.ts index aecd83d..518c960 100644 --- a/src/util/class.ts +++ b/src/util/class.ts @@ -10,47 +10,6 @@ import { AbstractClass } from "type-fest" export type StaticMembers = Pick -/** - * Copies properties from one class to another, including static and prototype properties. - * - * @param from - The source class to copy properties from. - * @param to - The destination class to copy properties to. - */ -export function copyClassProperties( - from: AbstractClass, - to: AbstractClass, -) { - Object.getOwnPropertyNames(from).forEach(name => { - // console.log(from, to, name, Object.getOwnPropertyDescriptor(from, name)) - - if (name === "name" - || name === "length" - || name === "prototype" - ) - return - - Object.defineProperty( - to, - name, - Object.getOwnPropertyDescriptor(from, name) || Object.create(null), - ) - }) - - Object.getOwnPropertyNames(from.prototype).forEach(name => { - // console.log(from, to, name, Object.getOwnPropertyDescriptor(from, name)) - - if (name === "constructor") - return - - Object.defineProperty( - to.prototype, - name, - Object.getOwnPropertyDescriptor(from.prototype, name) || Object.create(null), - ) - }) -} - - /** * Flattens the inheritance hierarchy of a given class by copying all properties * from its superclass chain into a single object. @@ -62,11 +21,16 @@ export function copyClassProperties( export function flattenClass< C extends AbstractClass >(class_: C) { - return getInheritanceHierarchy(class_) - .reduce((flattened, current) => { - copyClassProperties(current, flattened) - return flattened - }, class {}) as C + const flattenedClass = class {} as unknown as C + + getInheritanceHierarchy(class_).forEach(current => { + copyProperties(current, flattenedClass) + }) + + copyProperty(class_, flattenedClass, "name") + copyProperty(class_.prototype, flattenedClass.prototype, "constructor") + + return flattenedClass } @@ -74,7 +38,7 @@ export function flattenClass< * Retrieves the inheritance hierarchy of a given class, including itself. * * @param class_ - The class for which the inheritance hierarchy is retrieved. - * @returns An array representing the inheritance hierarchy. + * @returns An array representing the inheritance hierarchy, ordered from the furthest in the hierarchy to `class_` itself. */ export function getInheritanceHierarchy( class_: AbstractClass @@ -96,3 +60,50 @@ export function getInheritanceHierarchy( export function isClass(el: { toString: () => string }) { return Boolean(el.toString().match(/^class(?: [.\S]+)?(?: extends [.\S]+)? {[\s\S]*}$/)) } + + +/** + * Copies properties from one class to another, including static and prototype properties. + * + * @param from - The source class to copy properties from. + * @param to - The destination class to copy properties to. + */ +export function copyProperties( + from: AbstractClass, + to: AbstractClass, + ignoreKeys: string[] = [], + ignorePrototypeKeys: string[] = [], +) { + Object.getOwnPropertyNames(from).forEach(name => { + if (name === "prototype" + || ignoreKeys.find(v => v === name) + ) + return + + // console.log(from, to, name, Object.getOwnPropertyDescriptor(from, name)) + + copyProperty(from, to, name) + }) + + Object.getOwnPropertyNames(from.prototype).forEach(name => { + if (ignorePrototypeKeys.find(v => v === name)) + return + + // console.log(from, to, name, Object.getOwnPropertyDescriptor(from, name)) + + copyProperty(from.prototype, to.prototype, name) + }) +} + + +export function copyProperty( + from: unknown, + to: unknown, + name: string, +) { + Object.defineProperty( + to, + name, + Object.getOwnPropertyDescriptor(from, name) || Object.create(null), + ) +} -- 2.49.1 From a32c9c70f5359f60443753d77a037d8ef9f83006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 25 Dec 2023 01:46:38 +0100 Subject: [PATCH 07/21] makeLinkClass --- src/tests.ts | 24 +++++--- src/trait.ts | 153 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 108 insertions(+), 69 deletions(-) diff --git a/src/tests.ts b/src/tests.ts index a959718..5cbafb6 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -27,6 +27,10 @@ abstract class Identified { equals(el: Identified) { return this.id === el.id } + + // initializer() { + // console.log("Identified initializer") + // } } class ImplementsIdentifiable extends Identified { @@ -36,7 +40,13 @@ class ImplementsIdentifiable extends Identified { abstract class Permissible { 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(Permissible.constructor()) -// console.log(Object.getOwnPropertyNames(User.prototype)) +console.log(Permissible.constructor()) +console.log(Object.getOwnPropertyNames(User.prototype)) -// const user1 = new User(BigInt(1)) -// const user2 = new User(BigInt(2)) +const user1 = new User(BigInt(1)) +const user2 = new User(BigInt(2)) -// console.log(user1.equals(user2)) -// console.log(user1.permissions) +console.log(user1.equals(user2)) +console.log(user1.permissions) diff --git a/src/trait.ts b/src/trait.ts index 2b4b178..10cc87b 100644 --- a/src/trait.ts +++ b/src/trait.ts @@ -1,72 +1,85 @@ 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 = AbstractClass -// export function applyTrait< -// C extends Class | AbstractClass, -// TraitC extends Trait, -// >( -// class_: C, -// trait: TraitC, -// ) { -// copyClassProperties(trait, class_) - -// return class_ as ( -// (C extends Class -// ? Class< -// InstanceType & InstanceType, -// ConstructorParameters -// > -// : AbstractClass< -// InstanceType & InstanceType, -// ConstructorParameters -// > -// ) & -// StaticMembers & -// StaticMembers -// ) -// } - - -export const extendAndApplyTraits = < - C extends Class | AbstractClass, - Traits extends readonly Trait[], ->( - classToExtend: C, - traits: Traits, -) => - traits.reduce((class_, trait) => { - copyProperties(flattenClass(trait), class_) - return class_ - }, class extends classToExtend {}) as ( - AbstractClass< - InstanceType & - UnionToIntersection< - InstanceType - >, - - ConstructorParameters - > & - - StaticMembers< - C & - UnionToIntersection< - Traits[number] - > - > - ) - - +/** + * 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[] >( ...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 + | AbstractClass, + Traits extends readonly Trait[], +>( + 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[], + C extends Class + | AbstractClass + | 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 => { @@ -80,13 +93,29 @@ export function expresses< }) return class_ as unknown as ( - Trait< - UnionToIntersection< - InstanceType< - Traits[number] + (C extends Class | AbstractClass + ? ( + AbstractClass< + InstanceType & + UnionToIntersection< + InstanceType< + Traits[number] + > + >, + + ConstructorParameters + > & + + StaticMembers + ) + : Trait< + UnionToIntersection< + InstanceType< + Traits[number] + > > > - > & + ) & StaticMembers< UnionToIntersection< -- 2.49.1 From 55013a1110318efbcd77f6e0e19e108b799c0a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 25 Dec 2023 01:49:29 +0100 Subject: [PATCH 08/21] Generic fix --- src/tests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests.ts b/src/tests.ts index 5cbafb6..12ede12 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -51,7 +51,7 @@ abstract class Permissible { class User extends expresses( - Identified, + Identified as typeof Identified, // Identified, Permissible, ) { -- 2.49.1 From 018ece30f0e953053d0246d60da3ebee5dd5eafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 02:09:03 +0100 Subject: [PATCH 09/21] Attempt at trait constructor invocation --- bun.lockb | Bin 23850 -> 34056 bytes package.json | 1 + src/tests.ts | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index c6435276b23befef2cf5aff26ea03b90ecf1c67a..fbdbb10f55a5e68d64f08caca62e76bc09643100 100755 GIT binary patch delta 10521 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 delta 4019 zcmcIn3s6+&75?w#wF@jP%K{6c@)i)*U0_*Q;ED#Hm_}{K3sg~X!C*;sL>)totVv8U zGzog9CYVR9P8-amjSj}gHcq2WLn#z{5N#5QeWV@=xFQJ9GR&fUGiX_6+HrvKb? zzW?0+{O5n1^S`qR}4=4 zWXaa~LQ9pK)Uoo3icM?Mx^nTuEQD{xvi9~>%Y}G9M2OLlj{{SIzYfYj1tvpY1xy2~ zz%f76^^J(3V66to4g z-zY>Bu6rKQgjKC9;{FqY^XIRVCXauZTMO`7B{swd0Xlsa|fpYtw0I&;(^y-jsO1% zXaODr+JL#R$9f4s*4q^q=*SKr>u+JKLI#B*0r(+mvjJV<5(MtR87MFw0iw;%W@M|C z@O*+@fx)&QI^lER+*dDE(WO2*4BFPi4 z$YoK~30NIPen3q$4FNVslgFwUU&s7#g>XUzqY=|w3MrH&d7_DitctwOM4kji-esar zz;zS(0c&GuC_yp42iI1jf}~YQi>1y)MXrk_Kj6t&8Up-1mOM#{>@rg)V7Zz6fW2lK zN>YqQcyyXpeG*!q4QVQ*Fgg_Pmj4(>ew!j&EHni8o`pQgiu{;`IsrE=N}f@Q{E?M9M|ry74Z+0ecI3Fik`)+~7zg6? ze?TmuuP}@_6nP-V@gPo&SQw&!1tdn63t2$A9T5e>t^Exk>mi&p{c>KyoXP|6g@{lW zSZM-#l&A)As@C}bhHSS6yPB42z5qk0YIIT;H(MJly&ZWRvx5J1Wbs>e=vO<48~rAc z@y#3>bkNuLD0k;ubf`YH)tcG=?s3hdLA@_^X!h4~=srU1 z&Vfgevrv@`v=Q7$m%t;b+^$MdvqDrHw4SWn8 z0v}7sE|qc%Omv^CR!XO%E>+5)^ioyIqQQ+c^670@Q|Lb_b8(hY}+?)2136R6LlQoqwgHz7G`MukdsMJD=HMXgj!gOCix zCR$KgE4gTArAqrDnW}0fH`Q0Eba#n~4nis?qoR_{WukkOTB(8#C_VI4xQ%`?$zZ5P zY{p{O9Z0YFv$Q!pjvV2kKzNUyslIoJ;W*|2cG!0Y@noM)l__@4-?4SuC2dEfS0&;p$FN5UiIX1KX55N~q0w(&hhJ5!Q? zxZLo?Mo?4zsPJ2CK_+AUgf1Qq&jjuenm*gZ%}le4i;+kH@lf=x<|EH-WP&n4=^$Jk z`5MYsN|6TQGCo3bLD?YQt#UwIuL$G>@syw!kou=m zL&Kvaf4?MEzoh-{&6+Fg-~UmTEH%394m(WeN@Q-dmFm&5?1|T7*LBZ&9111&BB$Ny z&@Y}Bdd?)jwEfg^loZ*WcJ!uEqQ*v>+$PcNCR?h0aXrzu&b!U>`~pqS!FpX1JrBJe ziC%3iDt!WTAB70sGdSjaukV@tR!OOH+t;AKJ5Ykd+?P0zONA}{*J^9s3+UU-9O}orj*@cAqR+qsiBtCKpE2_U58g{e85pqUh7?hfY>#{Y9r-cq!4x zQ24xoG6bCoCa1)Zve=fYKZN$#e*gBVQ^W7ULJ@}LED^0Sv|@45QvK!Bv2ptFo?m3G z4K{)3_|I;erMF-kGZ}ad0hLDir8hn@yfacqe-0h)`p%_g@BF4Q*c_JTt~iSE=4Tzi zMVn(=e=kLx&8%JS={u;IK+lBu&_eUQMe>d~>h;=6^%v!qy|p_G?pLlu4=deecMJW6 zHUH?|ABPkk%?)+}o6DJay6ClKZAAd^>C$U`VSnGVNhVF9#E#W+V=7`Fscaghv^*4f hH}Bj?o9EMWEuYZY^?3s;mmiOy;nuQ&iEEBV{~Kd@;fMeL diff --git a/package.json b/package.json index 9868657..1762214 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "rollup": "^4.7.0", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-ts": "^3.4.5", + "tsx": "^4.7.0", "typescript": "^5.3.3" } } diff --git a/src/tests.ts b/src/tests.ts index 12ede12..523a9bd 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -40,8 +40,12 @@ class ImplementsIdentifiable extends Identified { abstract class Permissible { static readonly defaultPermissions: string[] = [] - // permissions: string[] = [] - permissions!: string[] + permissions: string[] = [] + // permissions!: string[] + + constructor() { + console.log("Permissible constructor") + } initializer() { console.log("Permissible initializer") @@ -63,13 +67,28 @@ class User extends expresses( } } -console.log(new User(BigInt(1))) +// const user1 = new User(BigInt(1)) +// const user2 = new User(BigInt(2)) -console.log(Permissible.constructor()) -console.log(Object.getOwnPropertyNames(User.prototype)) +// console.log(user1) +// console.log(user1.equals(user2)) -const user1 = new User(BigInt(1)) -const user2 = new User(BigInt(2)) -console.log(user1.equals(user2)) -console.log(user1.permissions) +class ConstructorTests1 { + value: string + + constructor() { + console.log("ConstructorTests1") + this.value = "ConstructorTests1" + } +} + +function ConstructorTests2(this: ConstructorTests1) { + console.log("ConstructorTests2") + this.value = "ConstructorTests2" +} + + +const targetObj = {} + +console.log(Reflect.construct(ConstructorTests1, [], ConstructorTests2)) -- 2.49.1 From 77d7dbe837adfe28be484771f0a9a34bc4682554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 02:27:51 +0100 Subject: [PATCH 10/21] Tests cleanup --- src/tests.ts | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/tests.ts b/src/tests.ts index 523a9bd..e86528d 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -67,28 +67,8 @@ class User extends expresses( } } -// const user1 = new User(BigInt(1)) -// const user2 = new User(BigInt(2)) +const user1 = new User(BigInt(1)) +const user2 = new User(BigInt(2)) -// console.log(user1) -// console.log(user1.equals(user2)) - - -class ConstructorTests1 { - value: string - - constructor() { - console.log("ConstructorTests1") - this.value = "ConstructorTests1" - } -} - -function ConstructorTests2(this: ConstructorTests1) { - console.log("ConstructorTests2") - this.value = "ConstructorTests2" -} - - -const targetObj = {} - -console.log(Reflect.construct(ConstructorTests1, [], ConstructorTests2)) +console.log(user1) +console.log(user1.equals(user2)) -- 2.49.1 From 3a155d5ef9cea7543b691bd56e8179a618340efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 04:03:08 +0100 Subject: [PATCH 11/21] Implementation using mixins --- src/tests-inheritance.ts | 45 ++++++++++++++++++++++++++++ src/trait-inheritance.ts | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/tests-inheritance.ts create mode 100644 src/trait-inheritance.ts diff --git a/src/tests-inheritance.ts b/src/tests-inheritance.ts new file mode 100644 index 0000000..0205309 --- /dev/null +++ b/src/tests-inheritance.ts @@ -0,0 +1,45 @@ +import { extendsAndExpresses, trait } from "./trait-inheritance" + + +const Identified = () => + trait(Parent => { + abstract class Identified extends Parent { + abstract id: ID + + equals(el: Identified) { + return this.id === el.id + } + } + + return Identified + }) + + +const Permissible = trait(Parent => { + abstract class Permissible extends Parent { + static readonly defaultPermissions: string[] = [] + permissions: string[] = [] + + constructor() { + super() + console.log("Permissible constructor") + } + } + + return Permissible +}) + + +const UserProto = extendsAndExpresses(class {}, [ + Identified(), + Permissible, +] as const) + +class User extends UserProto { + constructor(readonly id: bigint) { + super() + } +} + +const user1 = new User(1n) +user1.equals(user1) diff --git a/src/trait-inheritance.ts b/src/trait-inheritance.ts new file mode 100644 index 0000000..cee3cb1 --- /dev/null +++ b/src/trait-inheritance.ts @@ -0,0 +1,64 @@ +import { AbstractClass, Opaque, UnionToIntersection } from "type-fest" + + +/** + * Represents the static members of a class. + * + * @template C - The type of the class for which static members are extracted. + * @typeparam The static members of the class. + */ +export type StaticMembers = Pick + + +export type Trait> = + Opaque<(Parent: AbstractClass) => C, "Trait"> + +export type GetTraitC = + T extends Trait + ? C + : never + + +export function trait< + C extends AbstractClass +>( + trait: (Parent: AbstractClass) => C +) { + return trait as Trait +} + + +export function extendsAndExpresses< + C extends AbstractClass, + Traits extends readonly Trait[], +>( + extend: C, + traits: Traits, +) { + + return extend as unknown as ( + AbstractClass< + InstanceType & + UnionToIntersection< + InstanceType< + GetTraitC< + Traits[number] + > + > + >, + + ConstructorParameters + > & + + StaticMembers & + + StaticMembers< + UnionToIntersection< + GetTraitC< + Traits[number] + > + > + > + ) + +} -- 2.49.1 From f619f47fc351f9d828d173d687b69f5deab102ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 05:21:00 +0100 Subject: [PATCH 12/21] Working inheritance traits --- src/tests-inheritance.ts | 7 ++++--- src/trait-inheritance.ts | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tests-inheritance.ts b/src/tests-inheritance.ts index 0205309..85fd68f 100644 --- a/src/tests-inheritance.ts +++ b/src/tests-inheritance.ts @@ -20,8 +20,8 @@ const Permissible = trait(Parent => { static readonly defaultPermissions: string[] = [] permissions: string[] = [] - constructor() { - super() + constructor(...args: any[]) { + super(...args) console.log("Permissible constructor") } } @@ -42,4 +42,5 @@ class User extends UserProto { } const user1 = new User(1n) -user1.equals(user1) +console.log(user1) +console.log(user1.equals(user1)) diff --git a/src/trait-inheritance.ts b/src/trait-inheritance.ts index cee3cb1..569cdea 100644 --- a/src/trait-inheritance.ts +++ b/src/trait-inheritance.ts @@ -36,7 +36,10 @@ export function extendsAndExpresses< traits: Traits, ) { - return extend as unknown as ( + return traits.reduce( + (previous, trait) => trait(previous), + extend, + ) as ( AbstractClass< InstanceType & UnionToIntersection< -- 2.49.1 From 26adebda54612059e28f1c3ac18efd193e82041e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 05:35:45 +0100 Subject: [PATCH 13/21] Generic trait fix --- src/tests-inheritance.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests-inheritance.ts b/src/tests-inheritance.ts index 85fd68f..bc594ba 100644 --- a/src/tests-inheritance.ts +++ b/src/tests-inheritance.ts @@ -3,15 +3,15 @@ import { extendsAndExpresses, trait } from "./trait-inheritance" const Identified = () => trait(Parent => { - abstract class Identified extends Parent { + abstract class Identified extends Parent { abstract id: ID - equals(el: Identified) { + equals(el: Identified) { return this.id === el.id } } - return Identified + return Identified }) -- 2.49.1 From 5a706149ea97953cfece238cba4dda6bc54169fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 17:47:42 +0100 Subject: [PATCH 14/21] Fix --- src/trait-inheritance.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/trait-inheritance.ts b/src/trait-inheritance.ts index 569cdea..dc0c5a7 100644 --- a/src/trait-inheritance.ts +++ b/src/trait-inheritance.ts @@ -10,8 +10,12 @@ import { AbstractClass, Opaque, UnionToIntersection } from "type-fest" export type StaticMembers = Pick -export type Trait> = - Opaque<(Parent: AbstractClass) => C, "Trait"> +export type Trait< + C extends AbstractClass +> = Opaque< + (Parent: AbstractClass) => C, + "Trait" +> export type GetTraitC = T extends Trait @@ -54,7 +58,6 @@ export function extendsAndExpresses< > & StaticMembers & - StaticMembers< UnionToIntersection< GetTraitC< -- 2.49.1 From b7f31e62a46e0a997c61f5cc0b5540f6c09be4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 18:23:22 +0100 Subject: [PATCH 15/21] Trait applier --- src/trait-inheritance.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/trait-inheritance.ts b/src/trait-inheritance.ts index dc0c5a7..eb7a3dd 100644 --- a/src/trait-inheritance.ts +++ b/src/trait-inheritance.ts @@ -1,4 +1,4 @@ -import { AbstractClass, Opaque, UnionToIntersection } from "type-fest" +import { AbstractClass, AbstractConstructor, Opaque, UnionToIntersection } from "type-fest" /** @@ -11,24 +11,29 @@ export type StaticMembers = Pick export type Trait< - C extends AbstractClass + C extends AbstractClass > = Opaque< - (Parent: AbstractClass) => C, - "Trait" + TraitApplier, + "thilatrait/Trait" > -export type GetTraitC = +export type TraitApplier< + C extends AbstractClass +> = + (Parent: AbstractConstructor) => C + +export type UnwrapTraitC = T extends Trait ? C : never export function trait< - C extends AbstractClass + C extends AbstractClass >( - trait: (Parent: AbstractClass) => C + applier: TraitApplier ) { - return trait as Trait + return applier as Trait } @@ -48,7 +53,7 @@ export function extendsAndExpresses< InstanceType & UnionToIntersection< InstanceType< - GetTraitC< + UnwrapTraitC< Traits[number] > > @@ -60,7 +65,7 @@ export function extendsAndExpresses< StaticMembers & StaticMembers< UnionToIntersection< - GetTraitC< + UnwrapTraitC< Traits[number] > > -- 2.49.1 From 300b52e31c99446f5c16661b3a5f3c58f5ccdbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 18:58:01 +0100 Subject: [PATCH 16/21] Subtraiting tests --- src/tests-inheritance.ts | 28 ++++++++++++++++++++++------ src/trait-inheritance.ts | 12 +++++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/tests-inheritance.ts b/src/tests-inheritance.ts index bc594ba..8241b1a 100644 --- a/src/tests-inheritance.ts +++ b/src/tests-inheritance.ts @@ -1,7 +1,7 @@ -import { extendsAndExpresses, trait } from "./trait-inheritance" +import { expresses, trait } from "./trait-inheritance" -const Identified = () => +const Identifiable = () => trait(Parent => { abstract class Identified extends Parent { abstract id: ID @@ -9,11 +9,25 @@ const Identified = () => equals(el: Identified) { return this.id === el.id } + + constructor(...args: any[]) { + super(...args) + console.log("Identified constructor") + } } return Identified }) +const ImplementsIdentifiable = (defaultID: ID) => + trait(Parent => { + abstract class ImplementsIdentifiable extends Identifiable()(Parent) { + id: ID = defaultID + } + + return ImplementsIdentifiable + }) + const Permissible = trait(Parent => { abstract class Permissible extends Parent { @@ -30,14 +44,16 @@ const Permissible = trait(Parent => { }) -const UserProto = extendsAndExpresses(class {}, [ - Identified(), +const UserProto = expresses( + // Identifiable(), + ImplementsIdentifiable(0n), Permissible, -] as const) +) class User extends UserProto { - constructor(readonly id: bigint) { + constructor(id: bigint) { super() + this.id = id } } diff --git a/src/trait-inheritance.ts b/src/trait-inheritance.ts index eb7a3dd..f852ac8 100644 --- a/src/trait-inheritance.ts +++ b/src/trait-inheritance.ts @@ -38,13 +38,12 @@ export function trait< export function extendsAndExpresses< - C extends AbstractClass, + C extends AbstractClass, Traits extends readonly Trait[], >( extend: C, traits: Traits, ) { - return traits.reduce( (previous, trait) => trait(previous), extend, @@ -71,5 +70,12 @@ export function extendsAndExpresses< > > ) - +} + +export function expresses< + Traits extends readonly Trait[], +>( + ...traits: Traits +) { + return extendsAndExpresses(Object, traits) } -- 2.49.1 From d759b2cbba171819f7e7e8fb072f9cc82dd692ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 19:25:19 +0100 Subject: [PATCH 17/21] Moved old code to legacy/ --- src/index.ts | 82 ++++++++++++++++++++++++++++- src/legacy/tests.ts | 74 ++++++++++++++++++++++++++ src/{ => legacy}/trait.ts | 0 src/{ => legacy}/util/class.ts | 0 src/tests-inheritance.ts | 62 ---------------------- src/tests.ts | 94 +++++++++++++++------------------- src/trait-inheritance.ts | 81 ----------------------------- 7 files changed, 196 insertions(+), 197 deletions(-) create mode 100644 src/legacy/tests.ts rename src/{ => legacy}/trait.ts (100%) rename src/{ => legacy}/util/class.ts (100%) delete mode 100644 src/tests-inheritance.ts delete mode 100644 src/trait-inheritance.ts diff --git a/src/index.ts b/src/index.ts index d46d244..f852ac8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,81 @@ -export * from "./trait" +import { AbstractClass, AbstractConstructor, Opaque, UnionToIntersection } from "type-fest" + + +/** + * Represents the static members of a class. + * + * @template C - The type of the class for which static members are extracted. + * @typeparam The static members of the class. + */ +export type StaticMembers = Pick + + +export type Trait< + C extends AbstractClass +> = Opaque< + TraitApplier, + "thilatrait/Trait" +> + +export type TraitApplier< + C extends AbstractClass +> = + (Parent: AbstractConstructor) => C + +export type UnwrapTraitC = + T extends Trait + ? C + : never + + +export function trait< + C extends AbstractClass +>( + applier: TraitApplier +) { + return applier as Trait +} + + +export function extendsAndExpresses< + C extends AbstractClass, + Traits extends readonly Trait[], +>( + extend: C, + traits: Traits, +) { + return traits.reduce( + (previous, trait) => trait(previous), + extend, + ) as ( + AbstractClass< + InstanceType & + UnionToIntersection< + InstanceType< + UnwrapTraitC< + Traits[number] + > + > + >, + + ConstructorParameters + > & + + StaticMembers & + StaticMembers< + UnionToIntersection< + UnwrapTraitC< + Traits[number] + > + > + > + ) +} + +export function expresses< + Traits extends readonly Trait[], +>( + ...traits: Traits +) { + return extendsAndExpresses(Object, traits) +} diff --git a/src/legacy/tests.ts b/src/legacy/tests.ts new file mode 100644 index 0000000..e86528d --- /dev/null +++ b/src/legacy/tests.ts @@ -0,0 +1,74 @@ +import { AbstractClass } from "type-fest" +import { expresses } from "./trait" + + +function inspectClass(class_: AbstractClass) { + Object.getOwnPropertyNames(class_).forEach(name => { + console.log( + "[static]", + name, + Object.getOwnPropertyDescriptor(class_, name) + ) + }) + + Object.getOwnPropertyNames(class_.prototype).forEach(name => { + console.log( + "[prototype]", + name, + Object.getOwnPropertyDescriptor(class_.prototype, name) + ) + }) +} + + +abstract class Identified { + abstract id: ID + + equals(el: Identified) { + return this.id === el.id + } + + // initializer() { + // console.log("Identified initializer") + // } +} + +class ImplementsIdentifiable extends Identified { + id!: ID +} + + +abstract class Permissible { + static readonly defaultPermissions: string[] = [] + permissions: string[] = [] + // permissions!: string[] + + constructor() { + console.log("Permissible constructor") + } + + initializer() { + console.log("Permissible initializer") + this.permissions = [] + } +} + + +class User extends expresses( + Identified as typeof Identified, + // Identified, + Permissible, +) { + readonly id: bigint + + constructor(id: bigint) { + super() + this.id = id + } +} + +const user1 = new User(BigInt(1)) +const user2 = new User(BigInt(2)) + +console.log(user1) +console.log(user1.equals(user2)) diff --git a/src/trait.ts b/src/legacy/trait.ts similarity index 100% rename from src/trait.ts rename to src/legacy/trait.ts diff --git a/src/util/class.ts b/src/legacy/util/class.ts similarity index 100% rename from src/util/class.ts rename to src/legacy/util/class.ts diff --git a/src/tests-inheritance.ts b/src/tests-inheritance.ts deleted file mode 100644 index 8241b1a..0000000 --- a/src/tests-inheritance.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { expresses, trait } from "./trait-inheritance" - - -const Identifiable = () => - trait(Parent => { - abstract class Identified extends Parent { - abstract id: ID - - equals(el: Identified) { - return this.id === el.id - } - - constructor(...args: any[]) { - super(...args) - console.log("Identified constructor") - } - } - - return Identified - }) - -const ImplementsIdentifiable = (defaultID: ID) => - trait(Parent => { - abstract class ImplementsIdentifiable extends Identifiable()(Parent) { - id: ID = defaultID - } - - return ImplementsIdentifiable - }) - - -const Permissible = trait(Parent => { - abstract class Permissible extends Parent { - static readonly defaultPermissions: string[] = [] - permissions: string[] = [] - - constructor(...args: any[]) { - super(...args) - console.log("Permissible constructor") - } - } - - return Permissible -}) - - -const UserProto = expresses( - // Identifiable(), - ImplementsIdentifiable(0n), - Permissible, -) - -class User extends UserProto { - constructor(id: bigint) { - super() - this.id = id - } -} - -const user1 = new User(1n) -console.log(user1) -console.log(user1.equals(user1)) diff --git a/src/tests.ts b/src/tests.ts index e86528d..b19ad37 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1,74 +1,62 @@ -import { AbstractClass } from "type-fest" -import { expresses } from "./trait" +import { expresses, trait } from "." -function inspectClass(class_: AbstractClass) { - Object.getOwnPropertyNames(class_).forEach(name => { - console.log( - "[static]", - name, - Object.getOwnPropertyDescriptor(class_, name) - ) +const Identifiable = () => + trait(Parent => { + abstract class Identified extends Parent { + abstract id: ID + + equals(el: Identified) { + return this.id === el.id + } + + constructor(...args: any[]) { + super(...args) + console.log("Identified constructor") + } + } + + return Identified }) - Object.getOwnPropertyNames(class_.prototype).forEach(name => { - console.log( - "[prototype]", - name, - Object.getOwnPropertyDescriptor(class_.prototype, name) - ) +const ImplementsIdentifiable = (defaultID: ID) => + trait(Parent => { + abstract class ImplementsIdentifiable extends Identifiable()(Parent) { + id: ID = defaultID + } + + return ImplementsIdentifiable }) -} -abstract class Identified { - abstract id: ID +const Permissible = trait(Parent => { + abstract class Permissible extends Parent { + static readonly defaultPermissions: string[] = [] + permissions: string[] = [] - equals(el: Identified) { - return this.id === el.id + constructor(...args: any[]) { + super(...args) + console.log("Permissible constructor") + } } - // initializer() { - // console.log("Identified initializer") - // } -} - -class ImplementsIdentifiable extends Identified { - id!: ID -} + return Permissible +}) -abstract class Permissible { - static readonly defaultPermissions: string[] = [] - permissions: string[] = [] - // permissions!: string[] - - constructor() { - console.log("Permissible constructor") - } - - initializer() { - console.log("Permissible initializer") - this.permissions = [] - } -} - - -class User extends expresses( - Identified as typeof Identified, - // Identified, +const UserProto = expresses( + // Identifiable(), + ImplementsIdentifiable(0n), Permissible, -) { - readonly id: bigint +) +class User extends UserProto { constructor(id: bigint) { super() this.id = id } } -const user1 = new User(BigInt(1)) -const user2 = new User(BigInt(2)) - +const user1 = new User(1n) console.log(user1) -console.log(user1.equals(user2)) +console.log(user1.equals(user1)) diff --git a/src/trait-inheritance.ts b/src/trait-inheritance.ts deleted file mode 100644 index f852ac8..0000000 --- a/src/trait-inheritance.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { AbstractClass, AbstractConstructor, Opaque, UnionToIntersection } from "type-fest" - - -/** - * Represents the static members of a class. - * - * @template C - The type of the class for which static members are extracted. - * @typeparam The static members of the class. - */ -export type StaticMembers = Pick - - -export type Trait< - C extends AbstractClass -> = Opaque< - TraitApplier, - "thilatrait/Trait" -> - -export type TraitApplier< - C extends AbstractClass -> = - (Parent: AbstractConstructor) => C - -export type UnwrapTraitC = - T extends Trait - ? C - : never - - -export function trait< - C extends AbstractClass ->( - applier: TraitApplier -) { - return applier as Trait -} - - -export function extendsAndExpresses< - C extends AbstractClass, - Traits extends readonly Trait[], ->( - extend: C, - traits: Traits, -) { - return traits.reduce( - (previous, trait) => trait(previous), - extend, - ) as ( - AbstractClass< - InstanceType & - UnionToIntersection< - InstanceType< - UnwrapTraitC< - Traits[number] - > - > - >, - - ConstructorParameters - > & - - StaticMembers & - StaticMembers< - UnionToIntersection< - UnwrapTraitC< - Traits[number] - > - > - > - ) -} - -export function expresses< - Traits extends readonly Trait[], ->( - ...traits: Traits -) { - return extendsAndExpresses(Object, traits) -} -- 2.49.1 From b248f878a14b94e114100e7936ffd4b63bcf5342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Dec 2023 21:39:17 +0100 Subject: [PATCH 18/21] Added comments --- src/index.ts | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index f852ac8..9acfe8e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,13 +3,15 @@ import { AbstractClass, AbstractConstructor, Opaque, UnionToIntersection } from /** * Represents the static members of a class. - * - * @template C - The type of the class for which static members are extracted. - * @typeparam The static members of the class. + * @template C - The class type. */ export type StaticMembers = Pick +/** + * Represents a trait that can be applied to a class. + * @template C - The abstract class type. + */ export type Trait< C extends AbstractClass > = Opaque< @@ -17,17 +19,31 @@ export type Trait< "thilatrait/Trait" > +/** + * Represents the function signature for applying a trait to a parent class. + * @template C - The abstract class type. + */ export type TraitApplier< C extends AbstractClass > = (Parent: AbstractConstructor) => C +/** + * Unwraps the type of the class from a given trait. + * @template T - The trait type. + */ export type UnwrapTraitC = T extends Trait ? C : never +/** + * Creates a trait instance using the provided trait applier function. + * @template C - The abstract class type. + * @param applier - The trait applier function. + * @returns A trait instance. + */ export function trait< C extends AbstractClass >( @@ -37,6 +53,14 @@ export function trait< } +/** + * Extends a class with the given traits and expresses their combined functionality. + * @template C - The abstract class type. + * @template Traits - An array of trait instances. + * @param extend - The class to extend. + * @param traits - An array of trait instances to apply. + * @returns A new class type expressing the combined functionality of the base class and traits. + */ export function extendsAndExpresses< C extends AbstractClass, Traits extends readonly Trait[], @@ -72,6 +96,12 @@ export function extendsAndExpresses< ) } +/** + * Expresses the combined functionality of multiple traits. + * @template Traits - An array of trait instances. + * @param traits - An array of trait instances to apply. + * @returns A new class type expressing the combined functionality of the traits. + */ export function expresses< Traits extends readonly Trait[], >( -- 2.49.1 From 13c2113aa2359076feb29dc7a5a142d314b054e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 29 Dec 2023 00:22:59 +0100 Subject: [PATCH 19/21] Updated comments --- src/index.ts | 71 +++++++++++++++++++++++++++++++++++++++++++++++----- src/tests.ts | 8 +++--- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9acfe8e..fc70958 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,10 +39,45 @@ export type UnwrapTraitC = /** - * Creates a trait instance using the provided trait applier function. + * Creates a trait using the provided trait applier function. * @template C - The abstract class type. * @param applier - The trait applier function. - * @returns A trait instance. + * @returns A trait. + * @example + * Creates a trait: + * ```ts + * const Permissible = trait(Parent => { + * abstract class Permissible extends Parent { + * static readonly defaultPermissions: string[] = [] + * permissions: string[] = [] + * + * constructor(...args: any[]) { + * super(...args) + * } + * } + * + * return Permissible + * }) + * ``` + * Creates a generic trait: + * ```ts + * const Identifiable = () => + * trait(Parent => { + * abstract class Identifiable extends Parent { + * abstract readonly id: ID + * + * equals(el: Identifiable) { + * return this.id === el.id + * } + * + * constructor(...args: any[]) { + * super(...args) + * } + * } + * + * return Identifiable + * }) + * ``` */ export function trait< C extends AbstractClass @@ -56,10 +91,22 @@ export function trait< /** * Extends a class with the given traits and expresses their combined functionality. * @template C - The abstract class type. - * @template Traits - An array of trait instances. + * @template Traits - An array of traits. * @param extend - The class to extend. - * @param traits - An array of trait instances to apply. + * @param traits - An array of traits to apply. * @returns A new class type expressing the combined functionality of the base class and traits. + * @example + * Extends a superclass and applies traits: + * ```ts + * class User extends extendsAndExpresses(Entity, [Identifiable(), Permissible]) { + * readonly id: bigint + * + * constructor(id: bigint) { + * super() + * this.id = id + * } + * } + * ``` */ export function extendsAndExpresses< C extends AbstractClass, @@ -98,9 +145,21 @@ export function extendsAndExpresses< /** * Expresses the combined functionality of multiple traits. - * @template Traits - An array of trait instances. - * @param traits - An array of trait instances to apply. + * @template Traits - An array of trait. + * @param traits - An array of trait to apply. * @returns A new class type expressing the combined functionality of the traits. + * @example + * Applies traits to a class: + * ```ts + * class User extends expresses(Identifiable(), Permissible) { + * readonly id: bigint + * + * constructor(id: bigint) { + * super() + * this.id = id + * } + * } + * ``` */ export function expresses< Traits extends readonly Trait[], diff --git a/src/tests.ts b/src/tests.ts index b19ad37..37e9412 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -3,10 +3,10 @@ import { expresses, trait } from "." const Identifiable = () => trait(Parent => { - abstract class Identified extends Parent { - abstract id: ID + abstract class Identifiable extends Parent { + abstract readonly id: ID - equals(el: Identified) { + equals(el: Identifiable) { return this.id === el.id } @@ -16,7 +16,7 @@ const Identifiable = () => } } - return Identified + return Identifiable }) const ImplementsIdentifiable = (defaultID: ID) => -- 2.49.1 From 436e26eb19573176936383c1a6409ed356617734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 29 Dec 2023 00:55:59 +0100 Subject: [PATCH 20/21] CI build pipelines --- .drone.jsonnet | 90 ++++++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 8b33506..2a13019 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -19,9 +19,20 @@ local lint_step = { commands: ["bun lint:tsc"], }; -local generate_docker_tags_step = { - name: "generate-docker-tags", - image: "git.jvalver.de/thilawyn/drone-better-docker-autotag", +local build_step = { + name: "build", + image: bun_image, + commands: ["bun run build"], +}; + +local publish_step = { + name: "publish", + image: "plugins/npm", + + settings: { + registry: "https://git.jvalver.de/api/packages/jvalverde/npm", + token: { from_secret: "npm_token" }, + }, }; @@ -48,46 +59,45 @@ local generate_docker_tags_step = { ], }, - // Build the server and legacy API docker images without publishing them for pull requests - // { - // kind: "pipeline", - // type: "docker", - // name: "build-docker", + // Build the package without publishing for pull requests + { + kind: "pipeline", + type: "docker", + name: "build", - // trigger: { - // ref: { - // include: ["refs/pull/**"] - // } - // }, + trigger: { + ref: { + include: ["refs/pull/**"] + } + }, - // steps: [ - // fetch_step, - // generate_docker_tags_step, - // build_website_docker_step(false), - // build_legacy_api_docker_step(false), - // ], - // }, + steps: [ + install_step, + lint_step, + build_step, + ], + }, - // Build the server and legacy API docker images and publish them for master and tags - // { - // kind: "pipeline", - // type: "docker", - // name: "build-publish-docker", + // Build and publish the package for master and tags + { + kind: "pipeline", + type: "docker", + name: "build-publish", - // trigger: { - // ref: { - // include: [ - // "refs/heads/master", - // "refs/tags/**", - // ] - // } - // }, + trigger: { + ref: { + include: [ + "refs/heads/master", + "refs/tags/**", + ] + } + }, - // steps: [ - // fetch_step, - // generate_docker_tags_step, - // build_website_docker_step(true), - // build_legacy_api_docker_step(true), - // ], - // }, + steps: [ + install_step, + lint_step, + build_step, + publish_step, + ], + }, ] diff --git a/package.json b/package.json index 1762214..a33ddde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "thilatrait", - "version": "20231208.0.0", + "version": "20231229.0.0", "type": "module", "exports": { ".": { -- 2.49.1 From 72b5c97fd4064c30e181fa03049526057aabd47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 29 Dec 2023 01:04:01 +0100 Subject: [PATCH 21/21] Upgraded dependencies --- bun.lockb | Bin 34056 -> 152097 bytes package.json | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index fbdbb10f55a5e68d64f08caca62e76bc09643100..6908c42eda6b88f7986caaa7d40c5bde6ba3a027 100755 GIT binary patch literal 152097 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%0D-{aP(G*!R=|NI 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 D7Xe!A diff --git a/package.json b/package.json index a33ddde..f4bf5e0 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,13 @@ "clean:node": "rm -rf node_modules" }, "dependencies": { - "type-fest": "^4.8.3" + "type-fest": "^4.9.0" }, "devDependencies": { "bun-types": "latest", - "rollup": "^4.7.0", + "npm-check-updates": "^16.14.12", + "npm-sort": "^0.0.4", + "rollup": "^4.9.1", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-ts": "^3.4.5", "tsx": "^4.7.0", -- 2.49.1