import { Call, Fn, Pipe, Tuples } from "hotscript" import { AbstractClass } from "type-fest" import { Trait } from "./Trait" import { TraitExpression, emptyTraitExpression } from "./TraitExpression" import { ExtendableFn, StaticMembersFn } from "./util" type SpreadSupertraits[]> = ( Call< Tuples.FlatMap, Traits > ) interface PrependTraitSupertraitsFn extends Fn { return: [...Trait.Supertraits, this["arg0"]] } type AbstractMembersExtendable< Superclass extends AbstractClass<{}>, Traits extends Trait[], > = ( Pipe, Tuples.Prepend>, ]> ) type ImplInstanceExtendable< Superclass extends AbstractClass<{}>, Traits extends Trait[], > = ( Pipe, Tuples.Prepend>, ]> ) type ImplStaticMembersExtendable< Superclass extends AbstractClass<{}>, Traits extends Trait[], > = ( Pipe, Tuples.Prepend, Tuples.Map, ExtendableFn, ]> ) type BuildTraitExpression< Superclass extends AbstractClass<{}>, OwnTraits extends Trait[], AllTraits extends Trait[], > = ( AbstractMembersExtendable extends false ? "Type conflict between the traits abstract members and/or the superclass instance." : ImplInstanceExtendable extends false ? "Type conflict between the traits implementation instances and/or the superclass instance." : ImplStaticMembersExtendable extends false ? "Type conflict between the traits implementation static members and/or the superclass static members." : TraitExpression ) class TraitExpressionBuilder< Superclass extends AbstractClass<{}>, const OwnTraits extends Trait[], const AllTraits extends Trait[], > { constructor(private expression: TraitExpression) {} extends< Super extends AbstractClass >( superclass: Super ) { return new TraitExpressionBuilder( new TraitExpression( superclass, this.expression.ownTraits, this.expression.allTraits, ) ) } expresses< const Traits extends Trait[] >( ...traits: Traits ): TraitExpressionBuilder< Superclass, [...OwnTraits, ...Traits], [...AllTraits, ...SpreadSupertraits] > { return new TraitExpressionBuilder( new TraitExpression( this.expression.superclass, [...this.expression.ownTraits, ...traits], [...this.expression.allTraits, ...this.spreadSupertraits(traits)], ) ) } private spreadSupertraits< const Traits extends Trait< TraitExpression[]>, any, any, any >[] >( traits: Traits ) { return traits.flatMap(trait => [ ...trait.supertraits.allTraits, trait, ]) as SpreadSupertraits } build() { return this.expression as BuildTraitExpression } then(fn: (expression: ReturnType) => V): V { return fn(this.build()) } } export const expression = new TraitExpressionBuilder(emptyTraitExpression)