diff --git a/bun.lockb b/bun.lockb index cae4041..ccc9e1c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 2e38b31..ae34fb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@thilawyn/traitify-ts", - "version": "0.1.20", + "version": "0.1.21", "type": "module", "publishConfig": { "registry": "https://git.jvalver.de/api/packages/thilawyn/npm/" @@ -19,6 +19,24 @@ "types": "./dist/lib.d.cts", "default": "./dist/lib.cjs" } + }, + "./effect": { + "import": { + "types": "./dist/effect/lib.d.ts", + "default": "./dist/effect/lib.js" + }, + "require": { + "types": "./dist/effect/lib.d.cts", + "default": "./dist/effect/lib.cjs" + } + }, + "./traitsUnique-CJ9oW-Fc": { + "import": { + "types": "./dist/traitsUnique-CJ9oW-Fc.d.ts" + }, + "require": { + "types": "./dist/traitsUnique-CJ9oW-Fc.d.ts" + } } }, "scripts": { @@ -30,18 +48,18 @@ }, "dependencies": { "remeda": "^1.61.0", - "type-fest": "^4.18.0" + "type-fest": "^4.18.2" }, "devDependencies": { "bun-types": "latest", "npm-check-updates": "^16.14.20", "npm-sort": "^0.0.4", "tsup": "^8.0.2", - "tsx": "^4.7.3", + "tsx": "^4.10.0", "typescript": "^5.4.5" }, "optionalDependencies": { - "@effect/schema": "^0.66.12", - "effect": "^3.1.0" + "@effect/schema": "^0.67.0", + "effect": "^3.1.3" } } diff --git a/src/TraitExpression.ts b/src/TraitExpression.ts index b7a8f80..1198170 100644 --- a/src/TraitExpression.ts +++ b/src/TraitExpression.ts @@ -4,10 +4,20 @@ import { TraitBuilder } from "./TraitBuilder" import { Extend, StaticMembers } from "./util" +export type TraitExpressionLike< + Superclass extends AbstractClass, + Traits extends readonly Trait[], +> = { + readonly superclass: Superclass, + readonly traits: Traits, +} + + export class TraitExpression< Superclass extends AbstractClass, const Traits extends readonly Trait[], -> { +> +implements TraitExpressionLike { constructor( readonly superclass: Superclass, readonly traits: Traits, @@ -84,13 +94,13 @@ export namespace TraitExpression { } export type Superclass = ( - T extends TraitExpression + T extends TraitExpressionLike ? Superclass : never ) export type Traits = ( - T extends TraitExpression + T extends TraitExpressionLike ? Traits : never ) @@ -98,7 +108,7 @@ export namespace TraitExpression { export type Implements< - Exp extends TraitExpression[]> + Exp extends TraitExpressionLike[]> > = ( Simplify< Extend< @@ -110,7 +120,7 @@ export type Implements< ) export type StaticImplements< - Exp extends TraitExpression[]> + Exp extends TraitExpressionLike[]> > = ( Simplify< Extend< @@ -123,7 +133,7 @@ export type StaticImplements< export type TraitExpressionClass< - Exp extends TraitExpression[]> + Exp extends TraitExpressionLike[]> > = ( AbstractClass< TraitExpressionInstance, @@ -133,7 +143,7 @@ export type TraitExpressionClass< ) export type TraitExpressionConcreteClass< - Exp extends TraitExpression[]> + Exp extends TraitExpressionLike[]> > = ( Class< TraitExpressionInstance, @@ -143,7 +153,7 @@ export type TraitExpressionConcreteClass< ) export type TraitExpressionInstance< - Exp extends TraitExpression[]> + Exp extends TraitExpressionLike[]> > = ( InstanceType> & // Keep the instance of the superclass outside of any kind of type manipulation // as it can accidentely remove abstract properties @@ -155,7 +165,7 @@ export type TraitExpressionInstance< ) export type TraitExpressionStaticMembers< - Exp extends TraitExpression[]> + Exp extends TraitExpressionLike[]> > = ( Simplify< Extend<[ diff --git a/src/TraitExpressionBuilder.ts b/src/TraitExpressionBuilder.ts index c563268..464740d 100644 --- a/src/TraitExpressionBuilder.ts +++ b/src/TraitExpressionBuilder.ts @@ -1,7 +1,8 @@ -import { unique } from "remeda" -import { AbstractClass, IsEqual } from "type-fest" +import { AbstractClass } from "type-fest" import { Trait, TraitTuple } from "./Trait" import { TraitExpression } from "./TraitExpression" +import { SpreadSupertraits, spreadSupertraits } from "./spreadSupertraits" +import { TraitsUnique, traitsUnique } from "./traitsUnique" import { Extendable, StaticMembers } from "./util" @@ -9,56 +10,18 @@ export class TraitExpressionBuilder< Superclass extends AbstractClass, const Traits extends readonly Trait[], > { - declare ["constructor"]: typeof TraitExpressionBuilder - constructor( readonly expressionSuperclass: Superclass, readonly expressionTraits: Traits, ) {} - static spreadSupertraits< - const T extends readonly Trait< - TraitExpression< - typeof TraitExpression.NullSuperclass, - readonly Trait[] - >, - any, - any, - any - >[] - >( - traits: T - ) { - return traits.flatMap(trait => [ - ...trait.superExpression.traits, - trait, - ]) as readonly Trait[] as TraitExpressionBuilder.SpreadSupertraits - } - - static traitsUnique< - const T extends readonly Trait< - TraitExpression< - typeof TraitExpression.NullSuperclass, - readonly Trait[] - >, - any, - any, - any - >[] - >( - traits: T - ) { - return unique(traits) as readonly Trait[] as TraitExpressionBuilder.TraitsUnique - } - - extends< Super extends AbstractClass >( superclass: Super ) { - return new this.constructor( + return new TraitExpressionBuilder( superclass, this.expressionTraits, ) @@ -80,12 +43,12 @@ export class TraitExpressionBuilder< Superclass, TraitExpressionBuilder.ExpressesReturnTypeTraits > { - return new this.constructor( + return new TraitExpressionBuilder( this.expressionSuperclass, - this.constructor.traitsUnique([ + traitsUnique([ ...this.expressionTraits, - ...this.constructor.spreadSupertraits(traits), + ...spreadSupertraits(traits), ]), ) } @@ -106,11 +69,11 @@ export class TraitExpressionBuilder< Superclass, TraitExpressionBuilder.ExpressesFirstReturnTypeTraits > { - return new this.constructor( + return new TraitExpressionBuilder( this.expressionSuperclass, - this.constructor.traitsUnique([ - ...this.constructor.spreadSupertraits(traits), + traitsUnique([ + ...spreadSupertraits(traits), ...this.expressionTraits, ]), ) @@ -141,44 +104,13 @@ export class TraitExpressionBuilder< } export namespace TraitExpressionBuilder { - export type SpreadSupertraits = ( - Traits extends readonly [ - infer El extends Trait, - ...infer Rest, - ] - ? readonly [ - ...Trait.Supertraits, - El, - ...SpreadSupertraits, - ] - : readonly [] - ) - - export type TraitsUnique = ( - Traits extends readonly [ - ...infer Rest, - infer El extends Trait, - ] - ? IsTraitInTupleFromRight extends true - ? TraitsUnique - : readonly [...TraitsUnique, El] - : readonly [] - ) - type IsTraitInTupleFromRight = ( - Traits extends readonly [...infer Rest, infer El] - ? IsEqual extends true - ? true - : IsTraitInTupleFromRight - : false - ) - export type ExpressesReturnTypeTraits< Traits extends readonly Trait[], T extends readonly Trait[], > = ( - TraitExpressionBuilder.TraitsUnique, + ...SpreadSupertraits, ]> ) @@ -186,8 +118,8 @@ export namespace TraitExpressionBuilder { Traits extends readonly Trait[], T extends readonly Trait[], > = ( - TraitExpressionBuilder.TraitsUnique, + TraitsUnique, ...Traits, ]> ) diff --git a/src/effect.ts b/src/effect.ts deleted file mode 100644 index 53736ea..0000000 --- a/src/effect.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Schema as S } from "@effect/schema" -import { Simplify } from "type-fest" -import { Trait, TraitTuple } from "./Trait" -import { TraitExpression } from "./TraitExpression" -import { Extend } from "./util" - - -export function extendsEffectSchemaExpression< - Fields extends S.Struct.Fields, - A, I, R, C, - Inherited extends object, - Proto, - - const Traits extends readonly Trait[], ->( - expression: TraitExpression< - S.Class, - Traits - > -) { - return (): ( - S.Class< - Self, - Fields, - A, I, R, C, - Simplify< - Extend<[ - Inherited, - ...TraitTuple.MapImplInstance - ]> - >, - Proto - > & - - Simplify< - Extend> - > - ) => expression.extends as any -} diff --git a/src/effect/EffectSchemaTraitExpression.ts b/src/effect/EffectSchemaTraitExpression.ts new file mode 100644 index 0000000..a3d29ca --- /dev/null +++ b/src/effect/EffectSchemaTraitExpression.ts @@ -0,0 +1,84 @@ +import { Schema as S } from "@effect/schema" +import { AbstractConstructor, Simplify } from "type-fest" +import { Trait, TraitTuple } from "../Trait" +import { StaticImplements, TraitExpressionLike } from "../TraitExpression" +import { Extend, StaticMembers } from "../util" + + +export class EffectSchemaTraitExpression< + Fields extends S.Struct.Fields, + I, R, C, + Inherited extends object, + Proto, + Static extends object, + + const Traits extends readonly Trait[], + + const Mutability extends "Immutable" | "Mutable", + const EncodedMutability extends "Immutable" | "Mutable", +> +implements TraitExpressionLike< + S.Class, + Traits +> { + constructor( + readonly superclass: S.Class, + readonly superclassStatic: Static, + readonly traits: Traits, + readonly mutability: Mutability, + readonly encodedMutability: EncodedMutability, + ) {} + + + extends(): ( + AbstractConstructor< + ApplyMutability, Mutability> & + Omit & + Proto, + + ConstructorParameters> + > & + + StaticMembers< + S.Class< + Self, + Fields, + ApplyMutability, + R, C, + Simplify< + Extend<[ + Inherited, + ...TraitTuple.MapImplInstance + ]> + >, + Proto + > + > & + + Simplify< + Extend<[ + Static, + ...TraitTuple.MapImplStaticMembers + ]> + > + ) { + return this.traits.reduce( + (previous, trait) => trait.apply(previous), + this.superclass, + ) as any + } + + + staticImplements(_target: StaticImplements, _context: any) {} + + get staticImplementsStage2() { + return (_target: StaticImplements) => {} + } +} + + +type ApplyMutability = ( + Mutability extends "Immutable" + ? { +readonly [P in keyof T]: T[P] } + : { -readonly [P in keyof T]: T[P] } +) diff --git a/src/effect/EffectSchemaTraitExpressionBuilder.ts b/src/effect/EffectSchemaTraitExpressionBuilder.ts new file mode 100644 index 0000000..a8d6f4c --- /dev/null +++ b/src/effect/EffectSchemaTraitExpressionBuilder.ts @@ -0,0 +1,206 @@ +import { Schema as S } from "@effect/schema" +import { Simplify } from "type-fest" +import { Trait, TraitTuple } from "../Trait" +import { TraitExpression } from "../TraitExpression" +import { TraitExpressionBuilder } from "../TraitExpressionBuilder" +import { spreadSupertraits } from "../spreadSupertraits" +import { traitsUnique } from "../traitsUnique" +import { Extendable, StaticMembers } from "../util" +import { EffectSchemaTraitExpression } from "./EffectSchemaTraitExpression" + + +export class EffectSchemaInitialTraitExpressionBuilder { + class< + Fields extends S.Struct.Fields + >( + identifier: string, + fields: Fields, + annotations?: S.Annotations.Schema, + ) { + return new EffectSchemaTraitExpressionBuilder( + S.Class(identifier)(fields, annotations), + {}, + [], + "Immutable", + "Immutable", + ) + } + + taggedClass< + Tag extends string, + Fields extends S.Struct.Fields, + >( + tag: Tag, + fields: Fields, + annotations?: S.Annotations.Schema, + ) { + return new EffectSchemaTraitExpressionBuilder( + S.TaggedClass()(tag, fields, annotations), + {}, + [], + "Immutable", + "Immutable", + ) + } + + extends< + Super extends StaticMembers>, + Self extends object, + Fields extends S.Struct.Fields, + I, R, C, + Inherited extends object, + Proto, + + NewFields extends S.Struct.Fields, + >( + superclass: Super | StaticMembers>, + identifier: string, + fields: NewFields, + annotations?: S.Annotations.Schema, + ) { + return new EffectSchemaTraitExpressionBuilder( + superclass.extend(identifier)(fields, annotations), + + {} as Simplify< + Omit + > + >, + + [], + "Immutable", + "Immutable", + ) + } +} + + +export class EffectSchemaTraitExpressionBuilder< + Fields extends S.Struct.Fields, + I, R, C, + Inherited extends object, + Proto, + Static extends object, + + const Traits extends readonly Trait[], + + const Mutability extends "Immutable" | "Mutable", + const EncodedMutability extends "Immutable" | "Mutable", +> { + constructor( + readonly expressionSuperclass: S.Class, + readonly expressionSuperclassStatic: Static, + readonly expressionTraits: Traits, + readonly expressionMutability: Mutability, + readonly expressionEncodedMutability: EncodedMutability, + ) {} + + + mutable() { + return new EffectSchemaTraitExpressionBuilder( + this.expressionSuperclass, + this.expressionSuperclassStatic, + this.expressionTraits, + "Mutable", + this.expressionEncodedMutability, + ) + } + + immutable() { + return new EffectSchemaTraitExpressionBuilder( + this.expressionSuperclass, + this.expressionSuperclassStatic, + this.expressionTraits, + "Immutable", + this.expressionEncodedMutability, + ) + } + + mutableEncoded() { + return new EffectSchemaTraitExpressionBuilder( + this.expressionSuperclass, + this.expressionSuperclassStatic, + this.expressionTraits, + this.expressionMutability, + "Mutable", + ) + } + + immutableEncoded() { + return new EffectSchemaTraitExpressionBuilder( + this.expressionSuperclass, + this.expressionSuperclassStatic, + this.expressionTraits, + this.expressionMutability, + "Immutable", + ) + } + + + expresses< + const T extends readonly Trait< + TraitExpression< + typeof TraitExpression.NullSuperclass, + readonly Trait[] + >, + any, + any, + any + >[] + >( + ...traits: T + ) { + return new EffectSchemaTraitExpressionBuilder( + this.expressionSuperclass, + this.expressionSuperclassStatic, + + traitsUnique([ + ...this.expressionTraits, + ...spreadSupertraits(traits), + ]) as TraitExpressionBuilder.ExpressesReturnTypeTraits, + + this.expressionMutability, + this.expressionEncodedMutability, + ) + } + + + build(): ( + Extendable> extends false + ? "Type conflict between the traits abstract definitions." + : Extendable> extends false + ? "Type conflict between the traits static abstract definitions." + : Extendable<[ + InstanceType, + ...TraitTuple.MapImplInstance, + ]> extends false + ? "Type conflict between the traits implementation instance and/or the superclass instance." + : Extendable<[ + Static, + ...TraitTuple.MapImplStaticMembers, + ]> extends false + ? "Type conflict between the traits implementation static members and/or the superclass static members." + : EffectSchemaTraitExpression< + Fields, + I, R, C, + Inherited, + Proto, + Static, + + Traits, + Mutability, + EncodedMutability + > + ) { + return new EffectSchemaTraitExpression( + this.expressionSuperclass, + this.expressionSuperclassStatic, + this.expressionTraits, + this.expressionMutability, + this.expressionEncodedMutability, + ) as any + } +} + + +export const effectSchemaExpression = new EffectSchemaInitialTraitExpressionBuilder() diff --git a/src/effect/lib.ts b/src/effect/lib.ts new file mode 100644 index 0000000..1d4762b --- /dev/null +++ b/src/effect/lib.ts @@ -0,0 +1,2 @@ +export { EffectSchemaTraitExpression } from "./EffectSchemaTraitExpression" +export { EffectSchemaInitialTraitExpressionBuilder, EffectSchemaTraitExpressionBuilder, effectSchemaExpression } from "./EffectSchemaTraitExpressionBuilder" diff --git a/src/effect/tests.ts b/src/effect/tests.ts new file mode 100644 index 0000000..5052c90 --- /dev/null +++ b/src/effect/tests.ts @@ -0,0 +1,49 @@ +import { Schema as S } from "@effect/schema" +import { Implements } from "../TraitExpression" +import { EffectSchemaTraitExpression } from "./EffectSchemaTraitExpression" +import { effectSchemaExpression } from "./EffectSchemaTraitExpressionBuilder" + + +type RequiredKeys = { + [K in keyof T]-?: {} extends Pick ? never : K + }[keyof T] + +type InspectSchemaClass = T extends S.Class + ? Inherited + : never + + +const userExp = effectSchemaExpression + .class("User", { + id: S.BigIntFromSelf, + role: S.Union(S.Literal("User"), S.Literal("Admin")), + }) + .mutable() + .mutableEncoded() + .build() + +@userExp.staticImplements +export class User extends userExp.extends() implements Implements { + aMethodThatShouldBeInherited() {} + static aStaticMethodThatShouldBeInherited() {} +} + +User.Encoded + +const user = new User({ id: 0n, role: "User" }) +user.id = 0n + + +const adminExp = effectSchemaExpression + .extends(User, "User", { + role: S.Literal("Admin") + }) + // .immutable() + .build() + +@adminExp.staticImplements +export class Admin extends adminExp.extends() implements Implements { +} + +const admin = new Admin({ id: 1n, role: "Admin" }) +// admin.role = "Admin" diff --git a/src/lib.ts b/src/lib.ts index 32077ec..a96aa6a 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -2,4 +2,3 @@ export { Trait, TraitClass, TraitConcreteClass, TraitInstance, TraitStaticMember export { ImplStatic, TraitBuilder, implStaticInstantiableThis, implStaticThis, trait } from "./TraitBuilder" export { Implements, StaticImplements, TraitExpression, TraitExpressionClass, TraitExpressionConcreteClass, TraitExpressionInstance, TraitExpressionStaticMembers } from "./TraitExpression" export { TraitExpressionBuilder, expression } from "./TraitExpressionBuilder" -export { extendsEffectSchemaExpression } from "./effect" diff --git a/src/spreadSupertraits.ts b/src/spreadSupertraits.ts new file mode 100644 index 0000000..77c3377 --- /dev/null +++ b/src/spreadSupertraits.ts @@ -0,0 +1,35 @@ +import { Trait } from "./Trait" +import { TraitExpression } from "./TraitExpression" + + +export function spreadSupertraits< + const T extends readonly Trait< + TraitExpression< + typeof TraitExpression.NullSuperclass, + readonly Trait[] + >, + any, + any, + any + >[] +>( + traits: T +) { + return traits.flatMap(trait => [ + ...trait.superExpression.traits, + trait, + ]) as readonly Trait[] as SpreadSupertraits +} + +export type SpreadSupertraits = ( + Traits extends readonly [ + infer El extends Trait, + ...infer Rest, + ] + ? readonly [ + ...Trait.Supertraits, + El, + ...SpreadSupertraits, + ] + : readonly [] +) diff --git a/src/traitsUnique.ts b/src/traitsUnique.ts new file mode 100644 index 0000000..6df91cd --- /dev/null +++ b/src/traitsUnique.ts @@ -0,0 +1,39 @@ +import { unique } from "remeda" +import { IsEqual } from "type-fest" +import { Trait } from "./Trait" +import { TraitExpression } from "./TraitExpression" + + +export function traitsUnique< + const T extends readonly Trait< + TraitExpression< + typeof TraitExpression.NullSuperclass, + readonly Trait[] + >, + any, + any, + any + >[] +>( + traits: T +) { + return unique(traits) as readonly Trait[] as TraitsUnique +} + +export type TraitsUnique = ( + Traits extends readonly [ + ...infer Rest, + infer El extends Trait, + ] + ? IsTraitInTupleFromRight extends true + ? TraitsUnique + : readonly [...TraitsUnique, El] + : readonly [] +) +type IsTraitInTupleFromRight = ( + Traits extends readonly [...infer Rest, infer El] + ? IsEqual extends true + ? true + : IsTraitInTupleFromRight + : false +) diff --git a/tsup.config.ts b/tsup.config.ts index 3f59156..8d6a0de 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -2,10 +2,11 @@ import { defineConfig } from "tsup" export default defineConfig({ - entry: ["./src/lib.ts"], + entry: ["./src/lib.ts", "./src/effect/lib.ts"], format: ["esm", "cjs"], + skipNodeModulesBundle: true, dts: true, - splitting: false, + splitting: true, sourcemap: true, clean: true, })