import { Fn, Pipe, Tuples } from "hotscript" import { AbstractClass, Class, Opaque } from "type-fest" import { TraitExpression, emptyTraitExpression } from "./TraitExpression" import { AbstractTag } from "./abstract" import { ExtendFn, SimplifyFn, StaticMembers, StaticMembersFn } from "./util" export type TraitApplierSuperTag = "@thilawyn/traitify-ts/TraitApplierSuper" export type AddAbstractToImplClass< ImplClass extends Class<{}, []>, Abstract extends {}, > = ( Class< Abstract & InstanceType, ConstructorParameters > & StaticMembers ) export type RemoveAbstractFromImplClass< ImplClassWithAbstract extends Class & { _tag: TraitApplierSuperTag }, Abstract extends {}, > = ( Class< Omit, keyof Abstract>, ConstructorParameters > & Omit, "_tag"> ) export class Trait< Supertraits extends TraitExpression[], Trait[]>, Abstract extends {}, ImplClass extends Class<{}, []>, > { constructor( readonly supertraits: Supertraits, readonly abstract: Abstract, readonly apply: (Super: AbstractClass<{}>) => ImplClass, ) {} } export namespace Trait { export type OwnSupertraits = ( T extends Trait ? Supertraits : never ) export interface OwnSupertraitsFn extends Fn { return: Trait.OwnSupertraits } export type OwnAbstract = ( T extends Trait ? Abstract : never ) export interface OwnAbstractFn extends Fn { return: Trait.OwnAbstract } export type OwnImplClass = ( T extends Trait ? ImplClass : never ) export interface OwnImplClassFn extends Fn { return: Trait.OwnImplClass } export type OwnImplInstance = ( T extends Trait ? InstanceType : never ) export interface OwnImplInstanceFn extends Fn { return: Trait.OwnImplInstance } export type OwnClass = ( T extends Trait ? AddAbstractToImplClass : never ) export interface OwnClassFn extends Fn { return: Trait.OwnClass } export type OwnInstance = ( T extends Trait ? InstanceType< AddAbstractToImplClass > : never ) export interface OwnInstanceFn extends Fn { return: Trait.OwnInstance } export type Supertraits = ( T extends Trait ? TraitExpression.AllTraits : never ) export interface SupertraitsFn extends Fn { return: Trait.Supertraits } export type Class = ( AbstractClass< Trait.Instance, any[] > & Pipe, Tuples.Map, Tuples.Map, ExtendFn, SimplifyFn, ]> ) export interface ClassFn extends Fn { return: Trait.Class } export type Instance = ( Pipe, Tuples.Map, ExtendFn, SimplifyFn, ]> ) export interface InstanceFn extends Fn { return: Trait.Instance } } export function trait< Abstract extends {}, ImplClassWithAbstract extends Class & { _tag: TraitApplierSuperTag }, >( abstract: Opaque, apply: (Super: AbstractClass & { _tag: TraitApplierSuperTag }) => ImplClassWithAbstract, ) { return new Trait( emptyTraitExpression, abstract as Abstract, apply as any as (Super: AbstractClass<{}>) => RemoveAbstractFromImplClass, ) }