import { Call, Fn } from "hotscript" import { AbstractClass, Class, Opaque } from "type-fest" import { AbstractTag, TraitExpression, TraitExpressionAbstractFn, TraitExpressionNullSuperclass, TraitExpressionTraitsFn, emptyTraitExpression } from "." import { ExtendFn, StaticMembers } from "./util" type AddAbstractToImplClass< ImplClass extends Class<{}, []>, Abstract extends {}, > = ( Class< InstanceType & Abstract, ConstructorParameters > & StaticMembers ) type RemoveAbstractFromImplClass< ImplClassWithAbstract extends Class, Abstract extends {}, > = ( Class< Omit, keyof Abstract>, ConstructorParameters > & StaticMembers ) export class Trait< Super extends TraitExpression, Abstract extends {}, ImplClass extends Class<{}, []>, > { constructor( readonly superExpression: Super, readonly abstract: Abstract, readonly apply: (Super: AbstractClass<{}>) => ImplClass, ) {} } export interface TraitOwnAbstractFn extends Fn { return: this["arg0"] extends Trait ? Abstract : never } export type TraitOwnAbstract = Call export interface TraitOwnImplClassFn extends Fn { return: this["arg0"] extends Trait ? ImplClass : never } export type TraitOwnImplClass = Call export interface TraitOwnImplInstanceFn extends Fn { return: this["arg0"] extends Trait ? InstanceType : never } export type TraitOwnImplInstance = Call export interface TraitOwnClassFn extends Fn { return: this["arg0"] extends Trait ? AddAbstractToImplClass : never } export type TraitOwnClass = Call export interface TraitOwnInstanceFn extends Fn { return: this["arg0"] extends Trait ? InstanceType< AddAbstractToImplClass > : never } export type TraitOwnInstance = Call export interface TraitSupertraitsFn extends Fn { return: this["arg0"] extends Trait ? Call : never } export interface TraitAbstractFn extends Fn { return: this["arg0"] extends Trait ? Call, Abstract, ]> : never } export type TraitApplierSuperTag = "@thilawyn/traitify-ts/TraitApplierSuper" export function trait< Abstract extends {}, ImplClassWithAbstract extends Class, >( abstract: Opaque, apply: (Super: Opaque, TraitApplierSuperTag>) => ( Opaque ), ) { return new Trait( emptyTraitExpression, abstract as Abstract, apply as any as (Super: AbstractClass<{}>) => RemoveAbstractFromImplClass, ) }