import { uniq } from "lodash-es" import { AbstractClass } from "type-fest" import { Trait, TraitTuple } from "./Trait" import { TraitExpression } from "./TraitExpression" import { Extendable, StaticMembers } from "./util" type SpreadSupertraits = ( T extends [infer Trait, ...infer Rest] ? [ ...Trait.Supertraits, Trait, ...SpreadSupertraits, ] : [] ) type InstanceExtendable< Superclass extends AbstractClass, Traits extends Trait[], > = ( Extendable<[ InstanceType, ...TraitTuple.MapInstance, ]> ) type StaticMembersExtendable< Superclass extends AbstractClass, Traits extends Trait[], > = ( Extendable<[ StaticMembers, ...TraitTuple.MapStaticMembers, ]> ) type BuildTraitExpression< Superclass extends AbstractClass, Traits extends Trait[], > = ( InstanceExtendable extends false ? "Type conflict on the instance side." : StaticMembersExtendable extends false ? "Type conflict on the static side." : TraitExpression ) class TraitExpressionBuilder< Superclass extends AbstractClass, const Traits extends Trait[], > { constructor( private readonly expressionSuperclass: Superclass, private readonly expressionTraits: Traits, ) {} extends< Super extends AbstractClass >( superclass: Super ) { return new TraitExpressionBuilder( superclass, this.expressionTraits, ) } expresses< const T extends Trait< TraitExpression< typeof TraitExpression.NullSuperclass, Trait[] >, any, any, any >[] >( ...traits: T ) { return new TraitExpressionBuilder( this.expressionSuperclass, uniq([ ...this.expressionTraits, ...traits.flatMap(trait => [ ...trait.superExpression.traits, trait, ]), ]) as [...Traits, ...SpreadSupertraits], ) } build() { return new TraitExpression( this.expressionSuperclass, this.expressionTraits, ) as BuildTraitExpression } then(fn: (expression: ReturnType) => V): V { return fn(this.build()) } } export const expression = new TraitExpressionBuilder(TraitExpression.NullSuperclass, [])