From 7d1b5312f0d05e70eb483190cae3e70ccc70ed16 Mon Sep 17 00:00:00 2001 From: raveclassic Date: Sat, 9 Apr 2022 17:32:25 +0300 Subject: [PATCH] feat(core): trees --- packages/core/src/i.ts | 260 -------------------------------------- packages/core/src/j.ts | 130 ------------------- packages/core/src/test.ts | 229 ++++++++++++++++++++++++++++----- 3 files changed, 194 insertions(+), 425 deletions(-) delete mode 100644 packages/core/src/i.ts delete mode 100644 packages/core/src/j.ts diff --git a/packages/core/src/i.ts b/packages/core/src/i.ts deleted file mode 100644 index 51d79b7..0000000 --- a/packages/core/src/i.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { NoInfer, UnionToIntersection } from './utils' - -interface UnknownDependencyTree { - readonly name: PropertyKey | never - readonly type: unknown - readonly children: readonly UnknownDependencyTree[] - readonly optional: boolean -} - -interface Injectable { - (dependencies: NoInfer>): Value -} - -type InjectableValue = Target extends Injectable< - UnknownDependencyTree, - infer Value -> - ? Value - : never - -type InjectableDependencyTree = Target extends Injectable< - infer Tree, - unknown -> - ? Tree - : never - -type InjectableDependencies = RunFlattenTree< - InjectableDependencyTree -> - -type MapInjectablesToValues = { - readonly [Index in keyof Targets]: InjectableValue -} - -// type List = [Injectable] -// type F = MapInjectablesToValues - -type Deps = { - name: 'a' - type: 'result' - optional: false - children: [ - { - name: 'b' - type: string - optional: true - children: [ - { - name: 'c' - type: number - optional: false - children: [] - } - ] - }, - { - name: 'e' - type: string - optional: false - children: [] - } - ] -} -declare const deps: Deps -declare function testDeps(input: UnknownDependencyTree): void -testDeps(deps) - -type PickRequired = { - readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true - ? never - : Name]: DependencyTree['type'] -} -type PickOptional = { - readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true - ? Name - : never]?: DependencyTree['type'] -} - -type RunFlattenTree = DependencyTree extends never - ? { - readonly name: never - readonly type: never - readonly optional: false - readonly children: readonly [] - } - : DependencyTree extends UnknownDependencyTree - ? PickRequired & - PickOptional & - 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 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' - -declare function token( - name: Name -): () => Injectable< - { - readonly name: Name - readonly type: Type - readonly optional: false - 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 ? false : true - readonly children: { - readonly [Index in keyof Inputs]: InjectableDependencyTree - } -} - -declare function injectable< - Name extends PropertyKey, - Inputs extends readonly Injectable[], - Value ->( - name: Name, - ...args: readonly [ - ...Inputs, - (...values: MapInjectablesToValues) => Value - ] -): 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 RunOmitInChildren< - Children extends readonly UnknownDependencyTree[], - Keys -> = { - readonly [Index in keyof Children]: RunOmitDependencies -} - -type RunOmitDependencies = Tree extends UnknownDependencyTree - ? Tree['name'] extends Keys - ? never - : { - readonly type: Tree['type'] - readonly name: Tree['name'] - readonly optional: Tree['optional'] - readonly children: RunOmitInChildren - } - : 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 diff --git a/packages/core/src/j.ts b/packages/core/src/j.ts deleted file mode 100644 index 587835f..0000000 --- a/packages/core/src/j.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Merge, NoInfer, UnionToIntersection } from './utils' - -interface UnknownTree { - readonly name: PropertyKey | never - readonly type: unknown - readonly children: readonly UnknownTree[] - readonly optional: boolean -} - -interface Injectable { - (dependencies: NoInfer>>): Value -} - -interface UnknownInjectable { - (dependencies: Record): unknown -} - -type PickRequired = { - readonly [Name in Tree['name'] as Tree['optional'] extends true - ? never - : Name]: Tree['type'] -} -type PickOptional = { - readonly [Name in Tree['name'] as Tree['optional'] extends true - ? Name - : never]?: Tree['type'] -} -type FlattenTree = Tree extends UnknownTree - ? PickRequired & - PickOptional & - UnionToIntersection[number]> - : never -type FlattenChildren = { - readonly [Index in keyof Children]: FlattenTree -} - -export interface TokenAccessor { - >( - dependencies: Dependencies, - name: Name - ): Dependencies[Name] -} -export const TOKEN_ACCESSOR_KEY = '@injectable-ts/core//TOKEN_ACCESSOR' - -declare function token( - name: Name -): () => Injectable< - { - readonly name: Name - readonly type: Type - readonly optional: false - readonly children: readonly [ - { - readonly name: typeof TOKEN_ACCESSOR_KEY - readonly type: TokenAccessor - readonly optional: true - readonly children: readonly [] - } - ] - }, - Type -> - -type Deps = { - name: 'a' - type: 'result' - optional: false - children: [ - { - name: 'b' - type: string - optional: true - children: [ - { - name: 'c' - type: number - optional: false - children: [] - } - ] - }, - { - name: 'e' - type: string - optional: false - children: [] - } - ] -} -type Flat1 = Merge> - -type InjectableValue = ReturnType -type InjectableDependencies = Target extends UnknownInjectable - ? Parameters[0] - : never - -type _MapInjectablesToValues = { - readonly [Index in keyof Inputs]: InjectableDependencies -} -type MapInjectablesToValues = [ - InjectableDependencies -] - -type MergeDependencies< - Inputs extends readonly UnknownInjectable[], - Name extends PropertyKey | never, - Type -> = { - readonly name: Name - readonly type: Type - readonly optional: Name extends never ? false : true - readonly children: { - readonly [Index in keyof Inputs]: Inputs[Index] extends UnknownInjectable - ? InjectableDependencies - : never - } -} - -declare function injectable< - Name extends PropertyKey, - Inputs extends readonly Injectable[], - Value ->( - name: Name, - ...args: readonly [ - ...Inputs, - (...values: MapInjectablesToValues) => Value - ] -): void -injectable('foo', token('a')(), (a) => {}) diff --git a/packages/core/src/test.ts b/packages/core/src/test.ts index ca6aee9..cefd981 100644 --- a/packages/core/src/test.ts +++ b/packages/core/src/test.ts @@ -44,53 +44,212 @@ type InjectableDependencyTree = Target extends Injectable< ? Tree : never -type InjectableDependencies = Flatten> +type InjectableDependencies = Merge< + Flatten> +> type MapInjectablesToValues = { readonly [Index in keyof Targets]: InjectableValue } // -type Deps = { - name: 'a' - type: 'result' - optional: false - children: [ - { - name: 'b' - type: string - optional: true - children: [ - { - name: 'c' - type: number - optional: false - children: [] - } - ] - }, - { - name: 'e' - type: string - optional: false - children: [] - } - ] +type Foo = Injectable< + { + name: 'a' + type: 'result' + optional: false + children: [ + { + name: 'b' + type: string + optional: true + children: [ + { + name: 'c' + type: number + optional: false + children: [] + } + ] + }, + { + name: 'e' + type: string + optional: false + children: [] + } + ] + }, + Date +> + +export interface TokenAccessor { + >( + dependencies: Dependencies, + name: Name + ): Dependencies[Name] +} +export const TOKEN_ACCESSOR_KEY = '@injectable-ts/core//TOKEN_ACCESSOR' + +declare function token( + name: Name +): () => Injectable< + { + readonly name: Name + readonly type: Type + readonly optional: false + 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 ? false : true + readonly children: { + readonly [Index in keyof Inputs]: InjectableDependencyTree + } } -type Foo = Injectable -// $ExpectType Date -type FooValue = InjectableValue -type FooDependencies = Merge> -type InjectableFn< - Inputs extends readonly Injectable[] -> = (...args: MapInjectablesToValues) => void declare function injectable< + Name extends PropertyKey, Inputs extends readonly Injectable[], Value >( + name: Name, ...args: [...Inputs, (...values: MapInjectablesToValues) => Value] -): Injectable +): Injectable< + { + readonly [Key in keyof MergeDependencies< + Inputs, + never, + Value + >]: MergeDependencies[Key] + }, + Value +> +declare function injectable< + Inputs extends readonly Injectable[], + Value +>( + ...args: [...Inputs, (...values: MapInjectablesToValues) => Value] +): Injectable< + { + readonly [Key in keyof MergeDependencies< + Inputs, + never, + Value + >]: MergeDependencies[Key] + }, + Value +> declare const foo: Foo -const result = injectable(foo, (foo) => foo.toLocaleString()) +const result = injectable('result', foo, (foo) => foo.toLocaleString()) + +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 + +// +const r = a({ + e: 'e', + d: 'd', +}) + +// +type RunOmitInChildren< + Children extends readonly UnknownDependencyTree[], + Keys +> = { + readonly [Index in keyof Children]: RunOmitDependencies +} + +type RunOmitDependencies = Tree extends UnknownDependencyTree + ? Tree['name'] extends Keys + ? never + : { + readonly type: Tree['type'] + readonly name: Tree['name'] + readonly optional: Tree['optional'] + readonly children: RunOmitInChildren + } + : never + +declare function provide( + input: Injectable +): >() => Injectable< + { + readonly [Key in keyof RunOmitDependencies< + Dependencies, + Keys + >]: RunOmitDependencies[Key] + }, + (innerDependencies: { + readonly [Key in keyof Flatten as Key extends Keys + ? Key + : never]: Flatten[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 + +const rrrr = withoutDE({ + b: 'b', +})