import { Fn, Pipe, Tuples } from "hotscript" import { AbstractClass, Class, Opaque } from "type-fest" import { RemoveAbstractFromImplClass, Trait, TraitApplierSuperTag } from "./Trait" import { AbstractTag } from "./abstract" import { ExtendFn, SimplifyFn, StaticMembersFn } from "./util" // type RemoveSupertraitsAbstractFromAbstract = { // [Key in Extract]: Left[Key] // } & { // [Key in Exclude]: Left[Key] // } export class TraitExpression< Superclass extends AbstractClass<{}>, const OwnTraits extends Trait[], const AllTraits extends Trait[], > { constructor( readonly superclass: Superclass, readonly ownTraits: OwnTraits, readonly allTraits: AllTraits, ) {} get extends(): ( AbstractClass< Pipe, // Map all the traits to the instance of their implementation class Tuples.Prepend>, // 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 SimplifyFn, // Make readable for IDEs ]>, ConstructorParameters > & Pipe, // Map all the traits to their implementation class Tuples.Prepend, // Add the superclass at the top of the list Tuples.Map, // Map all the classes to an object containing their static members ExtendFn, // Reduce to a single object that extends all the objects in the list SimplifyFn, // Make readable for IDEs ]> ) { return this.allTraits.reduce( (previous, trait) => trait.apply(previous), this.superclass, ) as any } implementsStatic(target: ImplementsStatic, context: any) {} subtrait< This extends TraitExpression, SubtraitAbstract extends Implements, SubtraitStaticAbstract extends ImplementsStatic, SubtraitImplClassWithAbstract extends Class & SubtraitStaticAbstract & { _tag: TraitApplierSuperTag }, >( this: This, abstract: (expression: This) => Opaque, staticAbstract: (expression: This) => Opaque, apply: (Super: AbstractClass & SubtraitStaticAbstract & { _tag: TraitApplierSuperTag }) => SubtraitImplClassWithAbstract, ) { return new Trait( this, // {} as RemoveSupertraitsAbstractFromAbstract>, {} as SubtraitAbstract, // TODO: find a way to cleanly substract Implements from this. {} as SubtraitStaticAbstract, // TODO: find a way to cleanly substract StaticImplements from this. apply as any as (Super: AbstractClass<{}>) => RemoveAbstractFromImplClass, ) } } export namespace TraitExpression { export class NullSuperclass { static readonly _tag = "@thilawyn/traitify-ts/TraitExpression.NullSuperclass" } export type Superclass = ( T extends TraitExpression ? Superclass : never ) export interface SuperclassFn extends Fn { return: TraitExpression.Superclass } export type OwnTraits = ( T extends TraitExpression ? OwnTraits : never ) export interface OwnTraitsFn extends Fn { return: TraitExpression.OwnTraits } export type AllTraits = ( T extends TraitExpression ? AllTraits : never ) export interface AllTraitsFn extends Fn { return: TraitExpression.AllTraits } } export const emptyTraitExpression = new TraitExpression(TraitExpression.NullSuperclass, [], []) export type Implements> = ( Pipe, ExtendFn, SimplifyFn, ]> ) export type ImplementsStatic> = ( Pipe, ExtendFn, SimplifyFn, ]> )