Working subtraiting
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Julien Valverdé
2024-02-05 03:19:12 +01:00
parent a123da55fe
commit 6853bcbee8
7 changed files with 121 additions and 103 deletions

View File

@@ -4,7 +4,7 @@ import { AbstractTag, TraitExpression, emptyTraitExpression } from "."
import { StaticMembers } from "./util" import { StaticMembers } from "./util"
type AddAbstractToImplClass< export type AddAbstractToImplClass<
ImplClass extends Class<{}, []>, ImplClass extends Class<{}, []>,
Abstract extends {}, Abstract extends {},
> = ( > = (
@@ -15,7 +15,7 @@ type AddAbstractToImplClass<
StaticMembers<ImplClass> StaticMembers<ImplClass>
) )
type RemoveAbstractFromImplClass< export type RemoveAbstractFromImplClass<
ImplClassWithAbstract extends Class<Abstract, []>, ImplClassWithAbstract extends Class<Abstract, []>,
Abstract extends {}, Abstract extends {},
> = ( > = (

View File

@@ -1,7 +1,7 @@
import { Call, Fn, Pipe, Tuples } from "hotscript" import { Call, Fn, Pipe, Tuples } from "hotscript"
import { AbstractClass, Class, Opaque } from "type-fest" import { AbstractClass, Class, Opaque, Simplify } from "type-fest"
import { AbstractTag, Trait, TraitApplierSuperTag } from "." import { AbstractTag, RemoveAbstractFromImplClass, Trait, TraitApplierSuperTag } from "."
import { ExtendFn, SimplifyFn, StaticMembersFn } from "./util" import { CommonKeys, ExtendFn, KeysOnlyInLeft, SimplifyFn, StaticMembersFn } from "./util"
export class TraitExpression< export class TraitExpression<
@@ -44,18 +44,22 @@ export class TraitExpression<
} }
subtrait< subtrait<
SubtraitAbstract extends {}, SubtraitAbstract extends Implements<typeof this>,
SubtraitImplWithAbstract extends Class<{}>, SubtraitImplClassWithAbstract extends Class<SubtraitAbstract, []>,
>( >(
abstract: ( abstract: (expression: typeof this) => Opaque<SubtraitAbstract, AbstractTag>,
abstract: Pipe<typeof this, [ apply: (Super: Opaque<AbstractClass<SubtraitAbstract>, TraitApplierSuperTag>) => (
Opaque<SubtraitImplClassWithAbstract, TraitApplierSuperTag>
]> ),
) => Opaque<SubtraitAbstract, AbstractTag>,
// impl: ()
) { ) {
return new Trait(
this,
{} as Simplify<
CommonKeys<SubtraitAbstract, Implements<typeof this>> &
KeysOnlyInLeft<SubtraitAbstract, Implements<typeof this>>
>,
apply as any as (Super: AbstractClass<{}>) => RemoveAbstractFromImplClass<SubtraitImplClassWithAbstract, SubtraitAbstract>,
)
} }
} }
@@ -90,6 +94,12 @@ export namespace TraitExpression {
} }
} }
export const emptyTraitExpression = new TraitExpression(
TraitExpression.NullSuperclass,
[] as const,
[] as const,
)
export type Implements<Exp extends TraitExpression<any, any, any>> = ( export type Implements<Exp extends TraitExpression<any, any, any>> = (
Exp extends TraitExpression<any, infer AllTraits, any> Exp extends TraitExpression<any, infer AllTraits, any>
@@ -100,10 +110,3 @@ export type Implements<Exp extends TraitExpression<any, any, any>> = (
]> ]>
: never : never
) )
export const emptyTraitExpression = new TraitExpression(
TraitExpression.NullSuperclass,
[] as const,
[] as const,
)

View File

@@ -4,6 +4,67 @@ import { Trait, TraitExpression, emptyTraitExpression } from "."
import { ExtendableFn, StaticMembersFn } from "./util" import { ExtendableFn, StaticMembersFn } from "./util"
type SpreadSupertraits<Traits extends Trait<any, any, any>[]> = (
Call<
Tuples.FlatMap<PrependTraitSupertraitsFn>,
Traits
>
)
interface PrependTraitSupertraitsFn extends Fn {
return: [...Trait.Supertraits<this["arg0"]>, this["arg0"]]
}
type AbstractMembersExtendable<
Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[],
> = (
Call<ExtendableFn, [
InstanceType<Superclass>,
...Call<Tuples.Map<Trait.OwnAbstractFn>, Traits>,
]>
)
type ImplInstanceExtendable<
Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[],
> = (
Call<ExtendableFn, [
InstanceType<Superclass>,
...Call<Tuples.Map<Trait.OwnImplInstanceFn>, Traits>,
]>
)
type ImplStaticMembersExtendable<
Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[],
> = (
Pipe<[
Superclass,
...Call<Tuples.Map<Trait.OwnImplClassFn>, Traits>,
], [
Tuples.Map<StaticMembersFn>,
ExtendableFn,
]>
)
type BuildTraitExpression<
Superclass extends AbstractClass<{}>,
OwnTraits extends Trait<any, any, any>[],
AllTraits extends Trait<any, any, any>[],
> = (
Call<Tuples.IsEmpty, AllTraits> extends true
? "Cannot express an empty list of traits."
: AbstractMembersExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits abstract members and/or the superclass instance."
: ImplInstanceExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits implementation instances and/or the superclass instance."
: ImplStaticMembersExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits implementation static members and/or the superclass static members."
: TraitExpression<Superclass, OwnTraits, AllTraits>
)
class TraitExpressionBuilder< class TraitExpressionBuilder<
Super extends AbstractClass<{}>, Super extends AbstractClass<{}>,
OwnTraits extends Trait<any, any, any>[], OwnTraits extends Trait<any, any, any>[],
@@ -67,66 +128,4 @@ class TraitExpressionBuilder<
} }
} }
type SpreadSupertraits<Traits extends Trait<any, any, any>[]> = (
Call<
Tuples.FlatMap<PrependTraitSupertraitsFn>,
Traits
>
)
interface PrependTraitSupertraitsFn extends Fn {
return: [...Trait.Supertraits<this["arg0"]>, this["arg0"]]
}
type AbstractMembersExtendable<
Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[],
> = (
Call<ExtendableFn, [
InstanceType<Superclass>,
...Call<Tuples.Map<Trait.OwnAbstractFn>, Traits>,
]>
)
type ImplInstanceExtendable<
Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[],
> = (
Call<ExtendableFn, [
InstanceType<Superclass>,
...Call<Tuples.Map<Trait.OwnImplInstanceFn>, Traits>,
]>
)
type ImplStaticMembersExtendable<
Superclass extends AbstractClass<{}>,
Traits extends Trait<any, any, any>[],
> = (
Pipe<[
Superclass,
...Call<Tuples.Map<Trait.OwnImplClassFn>, Traits>,
], [
Tuples.Map<StaticMembersFn>,
ExtendableFn,
]>
)
type BuildTraitExpression<
Superclass extends AbstractClass<{}>,
OwnTraits extends Trait<any, any, any>[],
AllTraits extends Trait<any, any, any>[],
> = (
Call<Tuples.IsEmpty, AllTraits> extends true
? "Cannot express an empty list of traits."
: AbstractMembersExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits abstract members and/or the superclass instance."
: ImplInstanceExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits implementation instances and/or the superclass instance."
: ImplStaticMembersExtendable<Superclass, AllTraits> extends false
? "Type conflict between the traits implementation static members and/or the superclass static members."
: TraitExpression<Superclass, OwnTraits, AllTraits>
)
export const expression = new TraitExpressionBuilder(emptyTraitExpression) export const expression = new TraitExpressionBuilder(emptyTraitExpression)

View File

@@ -1,4 +1,4 @@
import { Implements, abstract, expression, trait } from "." import { Implements, Trait, abstract, expression, trait } from "."
const PrintsHelloOnNew = trait( const PrintsHelloOnNew = trait(
@@ -22,6 +22,7 @@ const Identifiable = <ID>() => trait(
const StatefulSubscription = trait( const StatefulSubscription = trait(
abstract<{ abstract<{
readonly isStatefulSubscription: true
readonly status: ( readonly status: (
{ _tag: "awaitingPayment" } | { _tag: "awaitingPayment" } |
{ _tag: "active", activeSince: Date, expiresAt?: Date } | { _tag: "active", activeSince: Date, expiresAt?: Date } |
@@ -32,14 +33,23 @@ const StatefulSubscription = trait(
Super => class StatefulSubscription extends Super {}, Super => class StatefulSubscription extends Super {},
) )
// interface ActiveStatefulSubscriptionAbstractMembers extends TraitAbstractMembers<typeof StatefulSubscription> { const ActiveStatefulSubscription = expression
// readonly status: { _tag: "active", activeSince: Date, expiresAt?: Date } .expresses(StatefulSubscription)
// } .build()
.subtrait(
exp => {
interface IActiveStatefulSubscription extends Implements<typeof exp> {
readonly isActiveStatefulSubscription: true
readonly status: { _tag: "active", activeSince: Date, expiresAt?: Date }
}
// const ActiveStatefulSubscription = trait( return abstract<IActiveStatefulSubscription>()
// abstract<ActiveStatefulSubscriptionAbstractMembers>(), },
// Super => class ActiveStatefulSubscription extends Super {},
// ) Super => class ActiveStatefulSubscription extends Super {},
)
type T = Trait.OwnAbstract<typeof ActiveStatefulSubscription>
class TestSuperclass { class TestSuperclass {
@@ -47,7 +57,6 @@ class TestSuperclass {
// static test = 69 // static test = 69
} }
const exp = expression const exp = expression
.extends(TestSuperclass) .extends(TestSuperclass)
.expresses( .expresses(
@@ -71,6 +80,7 @@ type Abs = Implements<typeof exp>
// ) // )
class User extends exp.extends implements Implements<typeof exp> { class User extends exp.extends implements Implements<typeof exp> {
readonly isStatefulSubscription: true = true
declare status: { _tag: "awaitingPayment" } | { _tag: "active"; activeSince: Date; expiresAt?: Date | undefined } | { _tag: "expired"; expiredSince: Date } declare status: { _tag: "awaitingPayment" } | { _tag: "active"; activeSince: Date; expiresAt?: Date | undefined } | { _tag: "expired"; expiredSince: Date }
id: bigint = -1n id: bigint = -1n
} }

View File

@@ -1,8 +1,7 @@
import { Call, ComposeLeft, Fn, Match, Tuples } from "hotscript" import { Call, ComposeLeft, Fn, Match, Tuples } from "hotscript"
import { CommonKeys } from "."
type CommonKeys<A, B> = Extract<keyof A, keyof B>
type ExtendReducer<Super, Self> = ( type ExtendReducer<Super, Self> = (
Pick<Self, CommonKeys<Self, Super>> extends Pick<Super, CommonKeys<Self, Super>> Pick<Self, CommonKeys<Self, Super>> extends Pick<Super, CommonKeys<Self, Super>>
? Omit<Super, CommonKeys<Self, Super>> & Self ? Omit<Super, CommonKeys<Self, Super>> & Self
@@ -13,7 +12,7 @@ interface ExtendReducerFn extends Fn {
} }
export type ExtendFn = Tuples.Reduce<ExtendReducerFn, {}> export type ExtendFn = Tuples.Reduce<ExtendReducerFn, {}>
export type Extend<T extends object[]> = Call<ExtendFn, T> export type Extend<T extends {}[]> = Call<ExtendFn, T>
export type ExtendableFn = ComposeLeft<[ export type ExtendableFn = ComposeLeft<[
@@ -23,4 +22,4 @@ export type ExtendableFn = ComposeLeft<[
Match.With<any, true>, Match.With<any, true>,
]> ]>
]> ]>
export type Extendable<T extends object[]> = Call<ExtendableFn, T> export type Extendable<T extends {}[]> = Call<ExtendableFn, T>

View File

@@ -1,9 +1,5 @@
/** import { CommonKeys } from "."
* Represents the common keys between two types.
* @template A - The first type.
* @template B - The second type.
*/
export type CommonKeys<A, B> = Extract<keyof A, keyof B>
/** /**
* Merges an inheritance tree defined by an array of types, considering overrides. * Merges an inheritance tree defined by an array of types, considering overrides.

View File

@@ -2,6 +2,17 @@ import { Fn } from "hotscript"
import { Simplify } from "type-fest" import { Simplify } from "type-fest"
/**
* Represents the common keys between two types.
* @template A - The first type.
* @template B - The second type.
*/
export type CommonKeys<A, B> = Extract<keyof A, keyof B>
export type KeysOnlyInLeft<Left, Right> = {
[K in Exclude<keyof Left, keyof Right>]: Left[K]
}
export interface SimplifyFn extends Fn { export interface SimplifyFn extends Fn {
return: Simplify<this["arg0"]> return: Simplify<this["arg0"]>
} }