From 6853bcbee8e6a133fc79eaacc41e95fd11a5c1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 5 Feb 2024 03:19:12 +0100 Subject: [PATCH] Working subtraiting --- src/Trait.ts | 4 +- src/TraitExpression.ts | 43 ++++++------ src/TraitExpressionBuilder.ts | 123 +++++++++++++++++----------------- src/tests.ts | 28 +++++--- src/util/extend.ts | 7 +- src/util/inheritance.ts | 8 +-- src/util/misc.ts | 11 +++ 7 files changed, 121 insertions(+), 103 deletions(-) diff --git a/src/Trait.ts b/src/Trait.ts index b3fdb42..5127d69 100644 --- a/src/Trait.ts +++ b/src/Trait.ts @@ -4,7 +4,7 @@ import { AbstractTag, TraitExpression, emptyTraitExpression } from "." import { StaticMembers } from "./util" -type AddAbstractToImplClass< +export type AddAbstractToImplClass< ImplClass extends Class<{}, []>, Abstract extends {}, > = ( @@ -15,7 +15,7 @@ type AddAbstractToImplClass< StaticMembers ) -type RemoveAbstractFromImplClass< +export type RemoveAbstractFromImplClass< ImplClassWithAbstract extends Class, Abstract extends {}, > = ( diff --git a/src/TraitExpression.ts b/src/TraitExpression.ts index 2599a3d..4c66717 100644 --- a/src/TraitExpression.ts +++ b/src/TraitExpression.ts @@ -1,7 +1,7 @@ import { Call, Fn, Pipe, Tuples } from "hotscript" -import { AbstractClass, Class, Opaque } from "type-fest" -import { AbstractTag, Trait, TraitApplierSuperTag } from "." -import { ExtendFn, SimplifyFn, StaticMembersFn } from "./util" +import { AbstractClass, Class, Opaque, Simplify } from "type-fest" +import { AbstractTag, RemoveAbstractFromImplClass, Trait, TraitApplierSuperTag } from "." +import { CommonKeys, ExtendFn, KeysOnlyInLeft, SimplifyFn, StaticMembersFn } from "./util" export class TraitExpression< @@ -44,18 +44,22 @@ export class TraitExpression< } subtrait< - SubtraitAbstract extends {}, - SubtraitImplWithAbstract extends Class<{}>, + SubtraitAbstract extends Implements, + SubtraitImplClassWithAbstract extends Class, >( - abstract: ( - abstract: Pipe - ) => Opaque, - - // impl: () + abstract: (expression: typeof this) => Opaque, + apply: (Super: Opaque, TraitApplierSuperTag>) => ( + Opaque + ), ) { - + return new Trait( + this, + {} as Simplify< + CommonKeys> & + KeysOnlyInLeft> + >, + apply as any as (Super: AbstractClass<{}>) => RemoveAbstractFromImplClass, + ) } } @@ -90,6 +94,12 @@ export namespace TraitExpression { } } +export const emptyTraitExpression = new TraitExpression( + TraitExpression.NullSuperclass, + [] as const, + [] as const, +) + export type Implements> = ( Exp extends TraitExpression @@ -100,10 +110,3 @@ export type Implements> = ( ]> : never ) - - -export const emptyTraitExpression = new TraitExpression( - TraitExpression.NullSuperclass, - [] as const, - [] as const, -) diff --git a/src/TraitExpressionBuilder.ts b/src/TraitExpressionBuilder.ts index 5aa95e0..a4b5b67 100644 --- a/src/TraitExpressionBuilder.ts +++ b/src/TraitExpressionBuilder.ts @@ -4,6 +4,67 @@ import { Trait, TraitExpression, emptyTraitExpression } from "." import { ExtendableFn, StaticMembersFn } from "./util" +type SpreadSupertraits[]> = ( + Call< + Tuples.FlatMap, + Traits + > +) +interface PrependTraitSupertraitsFn extends Fn { + return: [...Trait.Supertraits, this["arg0"]] +} + + +type AbstractMembersExtendable< + Superclass extends AbstractClass<{}>, + Traits extends Trait[], +> = ( + Call, + ...Call, Traits>, + ]> +) + +type ImplInstanceExtendable< + Superclass extends AbstractClass<{}>, + Traits extends Trait[], +> = ( + Call, + ...Call, Traits>, + ]> +) + +type ImplStaticMembersExtendable< + Superclass extends AbstractClass<{}>, + Traits extends Trait[], +> = ( + Pipe<[ + Superclass, + ...Call, Traits>, + ], [ + Tuples.Map, + ExtendableFn, + ]> +) + +type BuildTraitExpression< + Superclass extends AbstractClass<{}>, + OwnTraits extends Trait[], + AllTraits extends Trait[], +> = ( + Call extends true + ? "Cannot express an empty list of traits." + : AbstractMembersExtendable extends false + ? "Type conflict between the traits abstract members and/or the superclass instance." + : ImplInstanceExtendable extends false + ? "Type conflict between the traits implementation instances and/or the superclass instance." + : ImplStaticMembersExtendable extends false + ? "Type conflict between the traits implementation static members and/or the superclass static members." + : TraitExpression +) + + class TraitExpressionBuilder< Super extends AbstractClass<{}>, OwnTraits extends Trait[], @@ -67,66 +128,4 @@ class TraitExpressionBuilder< } } - -type SpreadSupertraits[]> = ( - Call< - Tuples.FlatMap, - Traits - > -) -interface PrependTraitSupertraitsFn extends Fn { - return: [...Trait.Supertraits, this["arg0"]] -} - - -type AbstractMembersExtendable< - Superclass extends AbstractClass<{}>, - Traits extends Trait[], -> = ( - Call, - ...Call, Traits>, - ]> -) - -type ImplInstanceExtendable< - Superclass extends AbstractClass<{}>, - Traits extends Trait[], -> = ( - Call, - ...Call, Traits>, - ]> -) - -type ImplStaticMembersExtendable< - Superclass extends AbstractClass<{}>, - Traits extends Trait[], -> = ( - Pipe<[ - Superclass, - ...Call, Traits>, - ], [ - Tuples.Map, - ExtendableFn, - ]> -) - -type BuildTraitExpression< - Superclass extends AbstractClass<{}>, - OwnTraits extends Trait[], - AllTraits extends Trait[], -> = ( - Call extends true - ? "Cannot express an empty list of traits." - : AbstractMembersExtendable extends false - ? "Type conflict between the traits abstract members and/or the superclass instance." - : ImplInstanceExtendable extends false - ? "Type conflict between the traits implementation instances and/or the superclass instance." - : ImplStaticMembersExtendable extends false - ? "Type conflict between the traits implementation static members and/or the superclass static members." - : TraitExpression -) - - export const expression = new TraitExpressionBuilder(emptyTraitExpression) diff --git a/src/tests.ts b/src/tests.ts index c80b6a2..e80c955 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1,4 +1,4 @@ -import { Implements, abstract, expression, trait } from "." +import { Implements, Trait, abstract, expression, trait } from "." const PrintsHelloOnNew = trait( @@ -22,6 +22,7 @@ const Identifiable = () => trait( const StatefulSubscription = trait( abstract<{ + readonly isStatefulSubscription: true readonly status: ( { _tag: "awaitingPayment" } | { _tag: "active", activeSince: Date, expiresAt?: Date } | @@ -32,14 +33,23 @@ const StatefulSubscription = trait( Super => class StatefulSubscription extends Super {}, ) -// interface ActiveStatefulSubscriptionAbstractMembers extends TraitAbstractMembers { -// readonly status: { _tag: "active", activeSince: Date, expiresAt?: Date } -// } +const ActiveStatefulSubscription = expression + .expresses(StatefulSubscription) + .build() + .subtrait( + exp => { + interface IActiveStatefulSubscription extends Implements { + readonly isActiveStatefulSubscription: true + readonly status: { _tag: "active", activeSince: Date, expiresAt?: Date } + } -// const ActiveStatefulSubscription = trait( -// abstract(), -// Super => class ActiveStatefulSubscription extends Super {}, -// ) + return abstract() + }, + + Super => class ActiveStatefulSubscription extends Super {}, + ) + +type T = Trait.OwnAbstract class TestSuperclass { @@ -47,7 +57,6 @@ class TestSuperclass { // static test = 69 } - const exp = expression .extends(TestSuperclass) .expresses( @@ -71,6 +80,7 @@ type Abs = Implements // ) class User extends exp.extends implements Implements { + readonly isStatefulSubscription: true = true declare status: { _tag: "awaitingPayment" } | { _tag: "active"; activeSince: Date; expiresAt?: Date | undefined } | { _tag: "expired"; expiredSince: Date } id: bigint = -1n } diff --git a/src/util/extend.ts b/src/util/extend.ts index a7ee625..d675de5 100644 --- a/src/util/extend.ts +++ b/src/util/extend.ts @@ -1,8 +1,7 @@ import { Call, ComposeLeft, Fn, Match, Tuples } from "hotscript" +import { CommonKeys } from "." -type CommonKeys = Extract - type ExtendReducer = ( Pick> extends Pick> ? Omit> & Self @@ -13,7 +12,7 @@ interface ExtendReducerFn extends Fn { } export type ExtendFn = Tuples.Reduce -export type Extend = Call +export type Extend = Call export type ExtendableFn = ComposeLeft<[ @@ -23,4 +22,4 @@ export type ExtendableFn = ComposeLeft<[ Match.With, ]> ]> -export type Extendable = Call +export type Extendable = Call diff --git a/src/util/inheritance.ts b/src/util/inheritance.ts index a179d78..8d9a516 100644 --- a/src/util/inheritance.ts +++ b/src/util/inheritance.ts @@ -1,9 +1,5 @@ -/** - * Represents the common keys between two types. - * @template A - The first type. - * @template B - The second type. - */ -export type CommonKeys = Extract +import { CommonKeys } from "." + /** * Merges an inheritance tree defined by an array of types, considering overrides. diff --git a/src/util/misc.ts b/src/util/misc.ts index d67c57e..eee119a 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -2,6 +2,17 @@ import { Fn } from "hotscript" 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 = Extract + +export type KeysOnlyInLeft = { + [K in Exclude]: Left[K] +} + export interface SimplifyFn extends Fn { return: Simplify }