From f79da8f7b0486aacab73a5e11663f54a448fad89 Mon Sep 17 00:00:00 2001 From: raveclassic Date: Sat, 9 Apr 2022 13:55:32 +0300 Subject: [PATCH] feat(core): trees --- packages/core/src/i.ts | 219 ++++++++++++++++++++++++++++++++++------- 1 file changed, 183 insertions(+), 36 deletions(-) diff --git a/packages/core/src/i.ts b/packages/core/src/i.ts index 12e5a1b..c45a876 100644 --- a/packages/core/src/i.ts +++ b/packages/core/src/i.ts @@ -1,16 +1,13 @@ -import { Merge, NoInfer, UnionToIntersection } from './utils' +import { Eq, Merge, NoInfer, UnionToIntersection } from './utils' interface UnknownDependencyTree { - readonly name: PropertyKey + readonly name: PropertyKey | never readonly type: unknown readonly children: readonly UnknownDependencyTree[] readonly optional?: true } -interface Injectable< - Dependencies extends readonly UnknownDependencyTree[], - Value -> { +interface Injectable { (dependencies: NoInfer): Value } @@ -19,6 +16,17 @@ type InjectableValue = Target extends Injectable ? Value : never +type InjectableDependencyTree = Target extends Injectable< + infer Tree, + any +> + ? Tree + : never + +type InjectableDependencies = RunFlattenTree< + InjectableDependencyTree +> + type MapInjectablesToValues< // eslint-disable-next-line @typescript-eslint/no-explicit-any Targets extends readonly Injectable[] @@ -53,42 +61,83 @@ declare const deps: Deps declare function testDeps(input: UnknownDependencyTree): void testDeps(deps) -type RunFlattenSingle = - DependencyTree extends UnknownDependencyTree - ? Merge< - { - readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true - ? never - : Name]: DependencyTree['type'] - } & { - readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true - ? Name - : never]?: DependencyTree['type'] - } & RunFlattenMany - > - : never - -type RunFlatten = { - readonly [Index in keyof DependencyTrees]: RunFlattenSingle< - DependencyTrees[Index] - > +type RunFlattenTree = DependencyTree extends never + ? { + readonly name: never + readonly type: never + readonly children: readonly [] + } + : DependencyTree extends UnknownDependencyTree + ? Merge< + { + readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true + ? never + : Name]: DependencyTree['type'] + } & { + readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true + ? Name + : never]?: DependencyTree['type'] + } & UnionToIntersection< + RunFlattenChildren[number] + > + > + : never + +type RunFlattenChildren = { + readonly [Index in keyof Children]: RunFlattenTree } -type RunFlattenMany = { - readonly [Key in keyof UnionToIntersection< - RunFlatten[number] - >]: UnionToIntersection[number]>[Key] +// type RunFlattenMany = { +// readonly [Key in keyof UnionToIntersection< +// RunFlatten[number] +// >]: UnionToIntersection[number]>[Key] +// } + +type t1 = RunFlattenTree +// type t2 = RunFlattenMany<[Deps]> + +export interface TokenAccessor { + >( + dependencies: Dependencies, + name: Name + ): Dependencies[Name] } +export const TOKEN_ACCESSOR_KEY = '@injectable-ts/core//TOKEN_ACCESSOR' -type t1 = RunFlattenSingle -type t2 = RunFlattenMany<[Deps]> +declare function token( + name: Name +): () => Injectable< + { + readonly name: Name + readonly type: Type + readonly children: readonly [ + { + readonly name: typeof TOKEN_ACCESSOR_KEY + readonly type: TokenAccessor + readonly optional: true + readonly children: readonly [] + } + ] + }, + Type +> + +type MergeDependencies< + Inputs extends readonly Injectable[], + Name extends PropertyKey | never, + Type +> = { + readonly name: Name + readonly type: Type + readonly optional: Name extends never ? undefined : true + readonly children: { + readonly [Index in keyof Inputs]: InjectableDependencyTree + } +} declare function injectable< Name extends PropertyKey, - Inputs extends readonly Injectable< - readonly UnknownDependencyTree[], - unknown - >[], + Inputs extends readonly Injectable[], Value >( name: Name, @@ -96,4 +145,102 @@ declare function injectable< ...Inputs, (...values: MapInjectablesToValues) => Value ] -): Injectable +): Injectable< + { + readonly [Key in keyof MergeDependencies< + Inputs, + Name, + Value + >]: MergeDependencies[Key] + }, + Value +> +declare function injectable< + Inputs extends readonly Injectable[], + Value +>( + ...args: readonly [ + ...Inputs, + (...values: MapInjectablesToValues) => Value + ] +): Injectable, Value> + +const d = token('d')<'d'>() +type D = typeof d +type DValue = InjectableValue +type DDependencies = InjectableDependencies + +const c = injectable('c', d, (d) => d) +type C = typeof c +type CValue = InjectableValue +type CDependencies = InjectableDependencies + +const b = injectable('b', c, (c) => c) +type B = typeof b +type BValue = InjectableValue +type BDependencies = InjectableDependencies + +const e = token('e')<'e'>() +type E = typeof e +type EValue = InjectableValue +type EDependencies = InjectableDependencies + +const a = injectable('a', b, e, (b, e) => `b: ${b}, e: ${e}` as const) +type A = typeof a +type AValue = InjectableValue +type ADependencies = InjectableDependencies + +type ATree = InjectableDependencyTree + +type OmitInChildren = { + readonly [Index in keyof Children]: RunOmitDependencies +} + +type RunOmitDependencies = Tree extends UnknownDependencyTree + ? Tree['name'] extends Keys + ? never + : Omit & { + readonly children: OmitInChildren + } + : never + +declare function provide( + input: Injectable +): >() => Injectable< + { + readonly [Key in keyof RunOmitDependencies< + Dependencies, + Keys + >]: RunOmitDependencies[Key] + }, + (innerDependencies: { + readonly [Key in keyof RunFlattenTree as Key extends Keys + ? Key + : never]: RunFlattenTree[Key] + }) => Value +> + +const withoutD = provide(a)<'d'>() +type WithoutD = typeof withoutD +type WithoutDValue = InjectableValue +type WithoutDDependencies = InjectableDependencies + +const withoutC = provide(a)<'c'>() +type WithoutC = typeof withoutC +type WithoutCValue = InjectableValue +type WithoutCDependencies = InjectableDependencies + +const withoutB = provide(a)<'b'>() +type WithoutB = typeof withoutB +type WithoutBValue = InjectableValue +type WithoutBDependencies = InjectableDependencies + +const withoutE = provide(a)<'e'>() +type WithoutE = typeof withoutE +type WithoutEValue = InjectableValue +type WithoutEDependencies = InjectableDependencies + +const withoutDE = provide(a)<'d' | 'e'>() +type WithoutDE = typeof withoutDE +type WithoutDEValue = InjectableValue +type WithoutDEDependencies = InjectableDependencies