import { Pipe, Tuples } from "hotscript" import { AbstractClass, Class } from "type-fest" import { Trait, TraitClass, TraitConcreteClass } from "./Trait" import { TraitExpression } from "./TraitExpression" import { ExtendFn, SimplifyFn, StaticMembers } from "./util" type ExtendAbstractSuper = ( Pipe, Tuples.Append, ExtendFn, SimplifyFn, ]> ) type ExtendStaticAbstractSuper = ( Pipe, Tuples.Append, ExtendFn, SimplifyFn, ]> ) type TraitApplierSuperTag = "@thilawyn/traitify-ts/Super" type RemoveAbstractFromImplClass< ImplClassWithAbstract extends AbstractClass, Abstract extends AbstractClass, > = ( Class< Omit< InstanceType, keyof InstanceType >, ConstructorParameters > & Omit< StaticMembers, keyof StaticMembers | "_tag" > ) export class TraitBuilder< SuperExpression extends TraitExpression< typeof TraitExpression.NullSuperclass, Trait[] >, Abstract extends object, StaticAbstract extends object, ImplClass extends Class, > { constructor( private readonly traitSuperExpression: SuperExpression, private readonly traitAbstract: Abstract, private readonly traitStaticAbstract: StaticAbstract, private readonly traitApply: (Super: AbstractClass) => ImplClass, ) {} abstract>() { return new TraitBuilder( this.traitSuperExpression, {} as A, this.traitStaticAbstract, this.traitApply, ) } extendAbstract>( _: ( Super: AbstractClass< ExtendAbstractSuper > ) => AbstractClass ) { return new TraitBuilder( this.traitSuperExpression, {} as A, this.traitStaticAbstract, this.traitApply, ) } staticAbstract>() { return new TraitBuilder( this.traitSuperExpression, this.traitAbstract, {} as A, this.traitApply, ) } extendStaticAbstract>( _: ( Super: AbstractClass< ExtendStaticAbstractSuper > ) => AbstractClass ) { return new TraitBuilder( this.traitSuperExpression, this.traitAbstract, {} as A, this.traitApply, ) } implement< ImplClassWithAbstract extends ( TraitConcreteClass< Trait > & { _tag: TraitApplierSuperTag } ) >( apply: ( Super: ( TraitClass< Trait > & { _tag: TraitApplierSuperTag } ) ) => ImplClassWithAbstract ) { return new TraitBuilder( this.traitSuperExpression, this.traitAbstract, this.traitStaticAbstract, apply as unknown as (Super: AbstractClass) => RemoveAbstractFromImplClass< ImplClassWithAbstract, TraitClass< Trait > >, ) } build() { return new Trait( this.traitSuperExpression, this.traitAbstract, this.traitStaticAbstract, this.traitApply, ) } } export const trait = new TraitBuilder( new TraitExpression(TraitExpression.NullSuperclass, []), {}, {}, Super => class extends Super {}, )