110 lines
2.8 KiB
TypeScript
110 lines
2.8 KiB
TypeScript
import { uniq } from "lodash-es"
|
|
import { AbstractClass } from "type-fest"
|
|
import { Trait, TraitTuple } from "./Trait"
|
|
import { TraitExpression } from "./TraitExpression"
|
|
import { Extendable, StaticMembers } from "./util"
|
|
|
|
|
|
type SpreadSupertraits<T> = (
|
|
T extends [infer Trait, ...infer Rest]
|
|
? [
|
|
...Trait.Supertraits<Trait>,
|
|
Trait,
|
|
...SpreadSupertraits<Rest>,
|
|
]
|
|
: []
|
|
)
|
|
|
|
|
|
type InstanceExtendable<
|
|
Superclass extends AbstractClass<object>,
|
|
Traits extends Trait<any, any, any, any>[],
|
|
> = (
|
|
Extendable<[
|
|
InstanceType<Superclass>,
|
|
...TraitTuple.MapInstance<Traits>,
|
|
]>
|
|
)
|
|
|
|
type StaticMembersExtendable<
|
|
Superclass extends AbstractClass<object>,
|
|
Traits extends Trait<any, any, any, any>[],
|
|
> = (
|
|
Extendable<[
|
|
StaticMembers<Superclass>,
|
|
...TraitTuple.MapStaticMembers<Traits>,
|
|
]>
|
|
)
|
|
|
|
type BuildTraitExpression<
|
|
Superclass extends AbstractClass<object>,
|
|
Traits extends Trait<any, any, any, any>[],
|
|
> = (
|
|
InstanceExtendable<Superclass, Traits> extends false
|
|
? "Type conflict on the instance side."
|
|
: StaticMembersExtendable<Superclass, Traits> extends false
|
|
? "Type conflict on the static side."
|
|
: TraitExpression<Superclass, Traits>
|
|
)
|
|
|
|
|
|
class TraitExpressionBuilder<
|
|
Superclass extends AbstractClass<object>,
|
|
const Traits extends Trait<any, any, any, any>[],
|
|
> {
|
|
constructor(
|
|
private readonly expressionSuperclass: Superclass,
|
|
private readonly expressionTraits: Traits,
|
|
) {}
|
|
|
|
extends<
|
|
Super extends AbstractClass<object>
|
|
>(
|
|
superclass: Super
|
|
) {
|
|
return new TraitExpressionBuilder(
|
|
superclass,
|
|
this.expressionTraits,
|
|
)
|
|
}
|
|
|
|
expresses<
|
|
const T extends Trait<
|
|
TraitExpression<
|
|
typeof TraitExpression.NullSuperclass,
|
|
Trait<any, any, any, any>[]
|
|
>,
|
|
any,
|
|
any,
|
|
any
|
|
>[]
|
|
>(
|
|
...traits: T
|
|
) {
|
|
return new TraitExpressionBuilder(
|
|
this.expressionSuperclass,
|
|
|
|
uniq([
|
|
...this.expressionTraits,
|
|
...traits.flatMap(trait => [
|
|
...trait.superExpression.traits,
|
|
trait,
|
|
]),
|
|
]) as [...Traits, ...SpreadSupertraits<T>],
|
|
)
|
|
}
|
|
|
|
build() {
|
|
return new TraitExpression(
|
|
this.expressionSuperclass,
|
|
this.expressionTraits,
|
|
) as BuildTraitExpression<Superclass, Traits>
|
|
}
|
|
|
|
then<V>(fn: (expression: ReturnType<typeof this.build>) => V): V {
|
|
return fn(this.build())
|
|
}
|
|
}
|
|
|
|
export const expression = new TraitExpressionBuilder(TraitExpression.NullSuperclass, [])
|