0.1.3 (#3)
All checks were successful
continuous-integration/drone/push Build is passing

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: https://git.jvalver.de/Thilawyn/traitify-ts/pulls/3
This commit was merged in pull request #3.
This commit is contained in:
Julien Valverdé
2024-02-08 20:01:16 +01:00
parent 98fbed09b3
commit c1c3c07524
10 changed files with 93 additions and 111 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@thilawyn/traitify-ts", "name": "@thilawyn/traitify-ts",
"version": "0.1.1", "version": "0.1.3",
"type": "module", "type": "module",
"publishConfig": { "publishConfig": {
"registry": "https://git.jvalver.de/api/packages/thilawyn/npm/" "registry": "https://git.jvalver.de/api/packages/thilawyn/npm/"
@@ -18,16 +18,6 @@
"types": "./dist/lib.d.cts", "types": "./dist/lib.d.cts",
"default": "./dist/lib.cjs" "default": "./dist/lib.cjs"
} }
},
"./util": {
"import": {
"types": "./dist/util.d.mts",
"default": "./dist/util.mjs"
},
"require": {
"types": "./dist/util.d.cts",
"default": "./dist/util.cjs"
}
} }
}, },
"scripts": { "scripts": {

View File

@@ -40,5 +40,4 @@ export const createBundleConfig = (
export default [ export default [
createBundleConfig("src/lib.ts", "."), createBundleConfig("src/lib.ts", "."),
createBundleConfig("src/util/lib.ts", "./util"),
] ]

View File

@@ -1,9 +1,13 @@
import { Fn, Pipe, Tuples } from "hotscript" import { Fn, Pipe, Tuples } from "hotscript"
import { AbstractClass, Class, Opaque } from "type-fest" import { AbstractClass, Class, Opaque } from "type-fest"
import { AbstractTag, TraitExpression, emptyTraitExpression } from "." import { TraitExpression, emptyTraitExpression } from "./TraitExpression"
import { AbstractTag } from "./abstract"
import { ExtendFn, SimplifyFn, StaticMembers, StaticMembersFn } from "./util" import { ExtendFn, SimplifyFn, StaticMembers, StaticMembersFn } from "./util"
export type TraitApplierSuperTag = "@thilawyn/traitify-ts/TraitApplierSuper"
export type AddAbstractToImplClass< export type AddAbstractToImplClass<
ImplClass extends Class<{}, []>, ImplClass extends Class<{}, []>,
Abstract extends {}, Abstract extends {},
@@ -16,14 +20,14 @@ export type AddAbstractToImplClass<
) )
export type RemoveAbstractFromImplClass< export type RemoveAbstractFromImplClass<
ImplClassWithAbstract extends Class<Abstract, []>, ImplClassWithAbstract extends Class<Abstract, []> & { _tag: TraitApplierSuperTag },
Abstract extends {}, Abstract extends {},
> = ( > = (
Class< Class<
Omit<InstanceType<ImplClassWithAbstract>, keyof Abstract>, Omit<InstanceType<ImplClassWithAbstract>, keyof Abstract>,
ConstructorParameters<ImplClassWithAbstract> ConstructorParameters<ImplClassWithAbstract>
> & > &
StaticMembers<ImplClassWithAbstract> Omit<StaticMembers<ImplClassWithAbstract>, "_tag">
) )
@@ -110,7 +114,9 @@ export namespace Trait {
Trait.Instance<T>, Trait.Instance<T>,
any[] any[]
> & > &
Pipe<[...Trait.Supertraits<T>, T], [ Pipe<T, [
Trait.SupertraitsFn,
Tuples.Append<T>,
Tuples.Map<Trait.OwnClassFn>, Tuples.Map<Trait.OwnClassFn>,
Tuples.Map<StaticMembersFn>, Tuples.Map<StaticMembersFn>,
ExtendFn, ExtendFn,
@@ -122,7 +128,9 @@ export namespace Trait {
} }
export type Instance<T> = ( export type Instance<T> = (
Pipe<[...Trait.Supertraits<T>, T], [ Pipe<T, [
Trait.SupertraitsFn,
Tuples.Append<T>,
Tuples.Map<Trait.OwnInstanceFn>, Tuples.Map<Trait.OwnInstanceFn>,
ExtendFn, ExtendFn,
SimplifyFn, SimplifyFn,
@@ -134,16 +142,12 @@ export namespace Trait {
} }
export type TraitApplierSuperTag = "@thilawyn/traitify-ts/TraitApplierSuper"
export function trait< export function trait<
Abstract extends {}, Abstract extends {},
ImplClassWithAbstract extends Class<Abstract, []>, ImplClassWithAbstract extends Class<Abstract, []> & { _tag: TraitApplierSuperTag },
>( >(
abstract: Opaque<Abstract, AbstractTag>, abstract: Opaque<Abstract, AbstractTag>,
apply: (Super: Opaque<AbstractClass<Abstract>, TraitApplierSuperTag>) => ( apply: (Super: AbstractClass<Abstract> & { _tag: TraitApplierSuperTag }) => ImplClassWithAbstract,
Opaque<ImplClassWithAbstract, TraitApplierSuperTag>
),
) { ) {
return new Trait( return new Trait(
emptyTraitExpression, emptyTraitExpression,

View File

@@ -1,6 +1,7 @@
import { Call, Fn, Pipe, Tuples } from "hotscript" import { Fn, Pipe, Tuples } from "hotscript"
import { AbstractClass, Class, Opaque } from "type-fest" import { AbstractClass, Class, Opaque } from "type-fest"
import { AbstractTag, RemoveAbstractFromImplClass, Trait, TraitApplierSuperTag } from "." import { RemoveAbstractFromImplClass, Trait, TraitApplierSuperTag } from "./Trait"
import { AbstractTag } from "./abstract"
import { ExtendFn, SimplifyFn, StaticMembersFn } from "./util" import { ExtendFn, SimplifyFn, StaticMembersFn } from "./util"
@@ -12,9 +13,9 @@ import { ExtendFn, SimplifyFn, StaticMembersFn } from "./util"
export class TraitExpression< export class TraitExpression<
Superclass extends AbstractClass<{}>, Superclass extends AbstractClass<{}>,
OwnTraits extends Trait<any, any, any>[], const OwnTraits extends Trait<any, any, any>[],
AllTraits extends Trait<any, any, any>[], const AllTraits extends Trait<any, any, any>[],
> { > {
constructor( constructor(
readonly superclass: Superclass, readonly superclass: Superclass,
@@ -24,40 +25,38 @@ export class TraitExpression<
get extends(): ( get extends(): (
AbstractClass< AbstractClass<
Pipe<[ Pipe<AllTraits, [
InstanceType<Superclass>, Tuples.Map<Trait.OwnImplInstanceFn>, // Map all the traits to the instance of their implementation class
...Call<Tuples.Map<Trait.OwnImplInstanceFn>, AllTraits>, Tuples.Prepend<InstanceType<Superclass>>, // Add the instance of the superclass at the top of the list
], [ ExtendFn, // Reduce to a single instance that extends all the instances in the list
ExtendFn, SimplifyFn, // Make readable for IDEs
SimplifyFn,
]>, ]>,
ConstructorParameters<Superclass> ConstructorParameters<Superclass>
> & > &
Pipe<[ Pipe<AllTraits, [
Superclass, Tuples.Map<Trait.OwnImplClassFn>, // Map all the traits to their implementation class
...Call<Tuples.Map<Trait.OwnImplClassFn>, AllTraits>, Tuples.Prepend<Superclass>, // Add the superclass at the top of the list
], [ Tuples.Map<StaticMembersFn>, // Map all the classes to an object containing their static members
Tuples.Map<StaticMembersFn>, ExtendFn, // Reduce to a single object that extends all the objects in the list
ExtendFn, SimplifyFn, // Make readable for IDEs
SimplifyFn,
]> ]>
) { ) {
return this.allTraits.reduce( return this.allTraits.reduce(
(previous, trait) => trait.apply(previous), (previous, trait) => trait.apply(previous),
this.superclass as Opaque<Superclass, TraitApplierSuperTag>, this.superclass,
) as any ) as any
} }
subtrait< subtrait<
SubtraitAbstract extends Implements<typeof this>, This extends TraitExpression<typeof TraitExpression.NullSuperclass, any, any>,
SubtraitImplClassWithAbstract extends Class<SubtraitAbstract, []>, SubtraitAbstract extends Implements<This>,
SubtraitImplClassWithAbstract extends Class<SubtraitAbstract, []> & { _tag: TraitApplierSuperTag },
>( >(
abstract: (expression: typeof this) => Opaque<SubtraitAbstract, AbstractTag>, this: This,
apply: (Super: Opaque<AbstractClass<SubtraitAbstract>, TraitApplierSuperTag>) => ( abstract: (expression: This) => Opaque<SubtraitAbstract, AbstractTag>,
Opaque<SubtraitImplClassWithAbstract, TraitApplierSuperTag> apply: (Super: AbstractClass<SubtraitAbstract> & { _tag: TraitApplierSuperTag }) => SubtraitImplClassWithAbstract,
),
) { ) {
return new Trait( return new Trait(
this, this,
@@ -69,7 +68,9 @@ export class TraitExpression<
} }
export namespace TraitExpression { export namespace TraitExpression {
export class NullSuperclass {} export class NullSuperclass {
static readonly _tag = "@thilawyn/traitify-ts/TraitExpression.NullSuperclass"
}
export type Superclass<T> = ( export type Superclass<T> = (
T extends TraitExpression<infer Superclass, any, any> T extends TraitExpression<infer Superclass, any, any>
@@ -99,19 +100,14 @@ export namespace TraitExpression {
} }
} }
export const emptyTraitExpression = new TraitExpression( export const emptyTraitExpression = new TraitExpression(TraitExpression.NullSuperclass, [], [])
TraitExpression.NullSuperclass,
[] as const,
[] as const,
)
export type Implements<Exp extends TraitExpression<any, any, any>> = ( export type Implements<Exp extends TraitExpression<any, any, any>> = (
Exp extends TraitExpression<any, infer AllTraits, any> Pipe<Exp, [
? Pipe<AllTraits, [ TraitExpression.AllTraitsFn,
Tuples.Map<Trait.OwnAbstractFn>, Tuples.Map<Trait.OwnAbstractFn>,
ExtendFn, ExtendFn,
SimplifyFn, SimplifyFn,
]> ]>
: never
) )

View File

@@ -1,6 +1,7 @@
import { Call, Fn, Pipe, Tuples } from "hotscript" import { Call, Fn, Pipe, Tuples } from "hotscript"
import { AbstractClass } from "type-fest" import { AbstractClass } from "type-fest"
import { Trait, TraitExpression, emptyTraitExpression } from "." import { Trait } from "./Trait"
import { TraitExpression, emptyTraitExpression } from "./TraitExpression"
import { ExtendableFn, StaticMembersFn } from "./util" import { ExtendableFn, StaticMembersFn } from "./util"
@@ -19,9 +20,9 @@ type AbstractMembersExtendable<
Superclass extends AbstractClass<{}>, Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[], Traits extends Trait<any, any, any>[],
> = ( > = (
Call<ExtendableFn, [ Pipe<Traits, [
InstanceType<Superclass>, Tuples.Map<Trait.OwnAbstractFn>,
...Call<Tuples.Map<Trait.OwnAbstractFn>, Traits>, Tuples.Prepend<InstanceType<Superclass>>,
]> ]>
) )
@@ -29,9 +30,9 @@ type ImplInstanceExtendable<
Superclass extends AbstractClass<{}>, Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[], Traits extends Trait<any, any, any>[],
> = ( > = (
Call<ExtendableFn, [ Pipe<Traits, [
InstanceType<Superclass>, Tuples.Map<Trait.OwnImplInstanceFn>,
...Call<Tuples.Map<Trait.OwnImplInstanceFn>, Traits>, Tuples.Prepend<InstanceType<Superclass>>,
]> ]>
) )
@@ -39,10 +40,9 @@ type ImplStaticMembersExtendable<
Superclass extends AbstractClass<{}>, Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[], Traits extends Trait<any, any, any>[],
> = ( > = (
Pipe<[ Pipe<Traits, [
Superclass, Tuples.Map<Trait.OwnImplClassFn>,
...Call<Tuples.Map<Trait.OwnImplClassFn>, Traits>, Tuples.Prepend<Superclass>,
], [
Tuples.Map<StaticMembersFn>, Tuples.Map<StaticMembersFn>,
ExtendableFn, ExtendableFn,
]> ]>
@@ -53,24 +53,22 @@ type BuildTraitExpression<
OwnTraits extends Trait<any, any, any>[], OwnTraits extends Trait<any, any, any>[],
AllTraits extends Trait<any, any, any>[], AllTraits extends Trait<any, any, any>[],
> = ( > = (
Call<Tuples.IsEmpty, AllTraits> extends true AbstractMembersExtendable<Superclass, AllTraits> extends false
? "Cannot express an empty list of traits." ? "Type conflict between the traits abstract members and/or the superclass instance."
: AbstractMembersExtendable<Superclass, AllTraits> extends false : ImplInstanceExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits abstract members and/or the superclass instance." ? "Type conflict between the traits implementation instances and/or the superclass instance."
: ImplInstanceExtendable<Superclass, AllTraits> extends false : ImplStaticMembersExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits implementation instances and/or the superclass instance." ? "Type conflict between the traits implementation static members and/or the superclass static members."
: ImplStaticMembersExtendable<Superclass, AllTraits> extends false : TraitExpression<Superclass, OwnTraits, AllTraits>
? "Type conflict between the traits implementation static members and/or the superclass static members."
: TraitExpression<Superclass, OwnTraits, AllTraits>
) )
class TraitExpressionBuilder< class TraitExpressionBuilder<
Super extends AbstractClass<{}>, Superclass extends AbstractClass<{}>,
OwnTraits extends Trait<any, any, any>[], const OwnTraits extends Trait<any, any, any>[],
AllTraits extends Trait<any, any, any>[], const AllTraits extends Trait<any, any, any>[],
> { > {
constructor(private expression: TraitExpression<Super, OwnTraits, AllTraits>) {} constructor(private expression: TraitExpression<Superclass, OwnTraits, AllTraits>) {}
extends< extends<
Super extends AbstractClass<any> Super extends AbstractClass<any>
@@ -87,25 +85,25 @@ class TraitExpressionBuilder<
} }
expresses< expresses<
Traits extends Trait<any, any, any>[] const Traits extends Trait<any, any, any>[]
>( >(
...traits: Traits ...traits: Traits
): TraitExpressionBuilder< ): TraitExpressionBuilder<
Super, Superclass,
[...OwnTraits, ...Traits], [...OwnTraits, ...Traits],
[...AllTraits, ...SpreadSupertraits<Traits>] [...AllTraits, ...SpreadSupertraits<Traits>]
> { > {
return new TraitExpressionBuilder( return new TraitExpressionBuilder(
new TraitExpression( new TraitExpression(
this.expression.superclass, this.expression.superclass,
[...this.expression.ownTraits, ...traits] as const, [...this.expression.ownTraits, ...traits],
[...this.expression.allTraits, ...this.spreadSupertraits(traits)] as const, [...this.expression.allTraits, ...this.spreadSupertraits(traits)],
) )
) as any )
} }
private spreadSupertraits< private spreadSupertraits<
Traits extends Trait< const Traits extends Trait<
TraitExpression<any, any, Trait<any, any, any>[]>, TraitExpression<any, any, Trait<any, any, any>[]>,
any, any,
any any
@@ -120,7 +118,7 @@ class TraitExpressionBuilder<
} }
build() { build() {
return this.expression as BuildTraitExpression<Super, OwnTraits, AllTraits> return this.expression as BuildTraitExpression<Superclass, OwnTraits, AllTraits>
} }
then<V>(fn: (expression: ReturnType<typeof this.build>) => V): V { then<V>(fn: (expression: ReturnType<typeof this.build>) => V): V {

View File

@@ -1,4 +0,0 @@
export * from "./Trait"
export * from "./TraitExpression"
export * from "./TraitExpressionBuilder"
export * from "./abstract"

View File

@@ -1,3 +1,4 @@
export { export { trait, type Trait } from "./Trait"
AbstractTag, Implements, TraitApplierSuperTag, abstract, expression, trait, type Trait, type TraitExpression export { Implements, type TraitExpression } from "./TraitExpression"
} from "." export { expression } from "./TraitExpressionBuilder"
export { abstract } from "./abstract"

View File

@@ -1,17 +1,21 @@
import { Simplify } from "type-fest" import { Trait, trait } from "./Trait"
import { Implements, Trait, abstract, expression, trait } from "." import { Implements } from "./TraitExpression"
import { Pipe } from "hotscript" import { expression } from "./TraitExpressionBuilder"
import { abstract } from "./abstract"
const PrintsHelloOnNew = trait( const PrintsHelloOnNew = trait(
abstract(), abstract(),
Super => class PrintsHelloOnNew extends Super { Super => class PrintsHelloOnNew extends Super {
static readonly isPrintsHelloOnNew = true
constructor(...args: any[]) { constructor(...args: any[]) {
super(...args) super(...args)
console.log("Hello!") console.log("Hello!")
} }
}, },
) )
type PrintsHelloOnNewClass = Trait.Class<typeof PrintsHelloOnNew>
const Identifiable = <ID>() => trait( const Identifiable = <ID>() => trait(
abstract<{ readonly id: ID }>(), abstract<{ readonly id: ID }>(),
@@ -34,6 +38,7 @@ const StatefulSubscription = trait(
Super => class StatefulSubscription extends Super {}, Super => class StatefulSubscription extends Super {},
) )
type StatefulSubscriptionClass = Trait.Class<typeof StatefulSubscription>
const ActiveStatefulSubscription = expression const ActiveStatefulSubscription = expression
.expresses(StatefulSubscription) .expresses(StatefulSubscription)
@@ -51,7 +56,7 @@ const ActiveStatefulSubscription = expression
Super => class ActiveStatefulSubscription extends Super {}, Super => class ActiveStatefulSubscription extends Super {},
) )
type T = Trait.Instance<typeof ActiveStatefulSubscription> type ActiveStatefulSubscriptionClass = Trait.Class<typeof ActiveStatefulSubscription>
class TestSuperclass { class TestSuperclass {

View File

@@ -1,3 +1,2 @@
export * from "./extend" export * from "./extend"
export * from "./inheritance"
export * from "./misc" export * from "./misc"

View File

@@ -1,6 +0,0 @@
export {
Extend,
ExtendFn,
Extendable,
ExtendableFn
} from "."