Skip to content

Commit

Permalink
breaking: improve Scope typing
Browse files Browse the repository at this point in the history
  • Loading branch information
Ni55aN committed May 7, 2023
1 parent befaa46 commit 0e2883e
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './editor'
export * as ClassicPreset from './presets/classic'
export type { ScopeAsParameter } from './scope'
export * from './scope'
export * from './types'
export * from './utility-types'
Expand Down
52 changes: 48 additions & 4 deletions src/scope.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
import { AcceptPartialUnion, Tail } from './utility-types'
/* eslint-disable @typescript-eslint/naming-convention */
import {
AcceptPartialUnion, CanAssignSignal, GetAssignmentReferences, GetNonAssignableElements, Tail
} from './utility-types'

export type Pipe<T> = (data: T) => Promise<undefined | T> | undefined | T

export type CanAssignEach<D extends any[], F extends any[]> = D extends [infer H1, ...infer Tail1]
? (
F extends [infer H2, ...infer Tail2] ?
[CanAssignSignal<H1, H2>, ...CanAssignEach<Tail1, Tail2>]
: []
): []

export type ScopeAsParameter<S extends Scope<any, any[]>, Current extends any[]> = (CanAssignEach<[S['__scope']['produces'], ...S['__scope']['parents']], Current>[number] extends true
? S
: 'Argument Scope does not provide expected signals'
)

/**
* Validate the Scope signals and replace the parameter type with an error message if they are not assignable
*/
export type NestedScope<S extends Scope<any, any[]>, Current extends any[]> = (CanAssignEach<Current, S['__scope']['parents']>[number] extends true
? S
: 'Parent signals do not satisfy the connected scope. Please use `.debug($ => $) for detailed assignment error'
)

/**
* Provides 'debug' method to check the detailed assignment error message
* @example .debug($ => $)
*/
export function useHelper<S extends Scope<any, any[]>, Signals>() {
type T1 = S['__scope']['parents'][number]
return {
debug<T extends GetNonAssignableElements<T1, Signals>>(f: (p: GetAssignmentReferences<T, Signals>) => T) {
f
}
}
}

export class Signal<T> {
pipes: Pipe<T>[] = []

Expand All @@ -24,26 +60,34 @@ export class Signal<T> {
export class Scope<Produces, Parents extends unknown[] = []> {
signal = new Signal<AcceptPartialUnion<Produces | Parents[number]>>()
parent?: any // Parents['length'] extends 0 ? undefined : Scope<Parents[0], Tail<Parents>>
__scope: {
produces: Produces
parents: Parents
}

constructor(public name: string) {}

addPipe(middleware: Pipe<Produces | Parents[number]>) {
this.signal.addPipe(middleware)
}

use<T>(scope: Scope<T, [Produces, ...Parents]>) {
use<S extends Scope<any, any[]>>(scope: NestedScope<S, [Produces, ...Parents]>) {
if (!(scope instanceof Scope)) throw new Error('cannot use non-Scope instance')

scope.setParent(this)
this.addPipe(context => {
return scope.signal.emit(context)
})

return useHelper<S, Produces | Parents[number]>()
}

setParent(scope: Scope<Parents[0], Tail<Parents>>) {
this.parent = scope
}

emit(context: Produces) {
return this.signal.emit(context)
emit<C extends Produces>(context: C): Promise<Extract<Produces, C>> {
return this.signal.emit(context) as Promise<Extract<Produces, C>>
}

hasParent(): boolean {
Expand Down
29 changes: 22 additions & 7 deletions src/utility-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type AcceptPartialUnion<T> = T | any

export type Tail<T extends any[]> = ((...args: T) => void) extends (head: any, ...tail: infer U) => any ? U : never

type UnionToIntersection<U> = (
export type UnionToIntersection<U> = (
U extends never ? never : (arg: U) => never
) extends (arg: infer I) => void
? I
Expand All @@ -13,30 +13,45 @@ type UnionToIntersection<U> = (
type StrictExcludeInner<T, U> = 0 extends (
U extends T ? [T] extends [U] ? 0 : never : never
) ? never : T
type StrictExclude<T, U> = T extends unknown ? StrictExcludeInner<T, U> : never
export type StrictExclude<T, U> = T extends unknown ? StrictExcludeInner<T, U> : never

type UnionToTuple<T> = UnionToIntersection<
export type UnionToTuple<T> = UnionToIntersection<
T extends never ? never : (t: T) => T
> extends (_: never) => infer W
? [...UnionToTuple<StrictExclude<T, W>>, W]
: []

type FilterMatch<T extends any[], V> = T extends [infer Head, ...infer _Tail]
export type FilterMatch<T extends any[], V> = T extends [infer Head, ...infer _Tail]
? ([Head] extends [V]
? [Head, ...FilterMatch<_Tail, V>]
: FilterMatch<_Tail, V>
): []

type CanAssignToAnyOf<Provides, Requires> = FilterMatch<UnionToTuple<Provides>, Requires> extends [] ? false : true
export type CanAssignToAnyOf<Provides, Requires> = FilterMatch<UnionToTuple<Provides>, Requires> extends [] ? false : true

type CanAssignEachTupleElemmentToAnyOf<Provides, Requires extends any[]> = Requires extends [infer Head, ...infer _Tail]
export type CanAssignEachTupleElemmentToAnyOf<Provides, Requires extends any[]> = Requires extends [infer Head, ...infer _Tail]
? CanAssignToAnyOf<Provides, Head> extends true ?
(_Tail extends []
? true
: CanAssignEachTupleElemmentToAnyOf<Provides, _Tail>
): false
: false

type CanAssignEachToAnyOf<Provides, Requires> = CanAssignEachTupleElemmentToAnyOf<Provides, UnionToTuple<Requires>>
export type CanAssignEachToAnyOf<Provides, Requires> = CanAssignEachTupleElemmentToAnyOf<Provides, UnionToTuple<Requires>>

export type CanAssignSignal<Provides, Requires> = CanAssignEachToAnyOf<Provides, Requires>

type ReplaceTupleTypes<T extends any[], U> = { [K in keyof T]: U }
export type FilterNever<T extends any[]> = T extends [infer Head, ...infer _Tail]
? ([Head] extends [never] ? FilterNever<_Tail> : [Head, ...FilterNever<_Tail>])
: []

type KeepIfNonAssignable<T, Signals> = CanAssignToAnyOf<Signals, T> extends false ? T : never

export type GetAllNonValidElements<T extends any[], Signals> = T extends [infer Head, ...infer _Tail]
? ([KeepIfNonAssignable<Head, Signals>, ...GetAllNonValidElements<_Tail, Signals>])
: []

export type GetNonAssignableElements<T, Signals>
= FilterNever<GetAllNonValidElements<UnionToTuple<T>, Signals>>
export type GetAssignmentReferences<AssignableElements extends any[], Signals> = ReplaceTupleTypes<AssignableElements, Signals>

0 comments on commit 0e2883e

Please sign in to comment.