From e51269651925dd8f42d0f0117892cbfd1d466d57 Mon Sep 17 00:00:00 2001 From: Andres Correa Casablanca Date: Sun, 20 Feb 2022 10:21:15 +0100 Subject: [PATCH 1/4] docs: say "super type safe" instead of functional --- README.md | 2 +- lambda-ioc/README.md | 2 +- lambda-ioc/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bdbf5dd..b710f96 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Known Vulnerabilities](https://snyk.io//test/github/Coder-Spirit/lambda-ioc/badge.svg?targetFile=package.json)](https://snyk.io//test/github/Coder-Spirit/lambda-ioc?targetFile=package.json) [![Security Score](https://snyk-widget.herokuapp.com/badge/npm/@coderspirit%2Flambda-ioc/badge.svg)](https://snyk.io/advisor/npm-package/@coderspirit/lambda-ioc) -> Pure functional (λ) dependency injection 💉 for TypeScript (inspired by Diddly) +> Super type safe dependency injection 💉 for TypeScript (inspired by Diddly) **NOTE:** This is a "fork" of Tom Sherman's **[Diddly library](https://github.com/tom-sherman/diddly)**, who deserves most diff --git a/lambda-ioc/README.md b/lambda-ioc/README.md index bdbf5dd..b710f96 100644 --- a/lambda-ioc/README.md +++ b/lambda-ioc/README.md @@ -7,7 +7,7 @@ [![Known Vulnerabilities](https://snyk.io//test/github/Coder-Spirit/lambda-ioc/badge.svg?targetFile=package.json)](https://snyk.io//test/github/Coder-Spirit/lambda-ioc?targetFile=package.json) [![Security Score](https://snyk-widget.herokuapp.com/badge/npm/@coderspirit%2Flambda-ioc/badge.svg)](https://snyk.io/advisor/npm-package/@coderspirit/lambda-ioc) -> Pure functional (λ) dependency injection 💉 for TypeScript (inspired by Diddly) +> Super type safe dependency injection 💉 for TypeScript (inspired by Diddly) **NOTE:** This is a "fork" of Tom Sherman's **[Diddly library](https://github.com/tom-sherman/diddly)**, who deserves most diff --git a/lambda-ioc/package.json b/lambda-ioc/package.json index 1549f03..d1cb467 100644 --- a/lambda-ioc/package.json +++ b/lambda-ioc/package.json @@ -9,7 +9,7 @@ "require": "./dist/cjs/index.js", "node": "./dist/cjs/index.js" }, - "description": "Pure functional (λ) dependency injection 💉 for TypeScript (inspired by Diddly)", + "description": "Super type safe dependency injection 💉 for TypeScript (inspired by Diddly)", "keywords": [ "typescript", "functional", From 3dec90bc84daa8c3910417e9088cf6904d3adbba Mon Sep 17 00:00:00 2001 From: Andres Correa Casablanca Date: Sun, 20 Feb 2022 14:06:37 +0100 Subject: [PATCH 2/4] feat: indirect self-resolution --- lambda-ioc/deno/combinators.ts | 3 +- lambda-ioc/deno/container.ts | 178 +++++++++++++++++++-- lambda-ioc/deno/util.ts | 21 +-- lambda-ioc/src/__tests__/container.test.ts | 90 ++++++++++- lambda-ioc/src/combinators.ts | 3 +- lambda-ioc/src/container.ts | 178 +++++++++++++++++++-- lambda-ioc/src/util.ts | 21 +-- 7 files changed, 433 insertions(+), 61 deletions(-) diff --git a/lambda-ioc/deno/combinators.ts b/lambda-ioc/deno/combinators.ts index 44334f6..1207401 100644 --- a/lambda-ioc/deno/combinators.ts +++ b/lambda-ioc/deno/combinators.ts @@ -1,10 +1,11 @@ import { AsyncDependencyFactory, + ContainerKey, ReadableContainer, ReadableSyncContainer, SyncDependencyFactory, } from './container.ts'; -import { ContainerKey, ParamsToResolverKeys, TupleO, Zip } from './util.ts'; +import { ParamsToResolverKeys, TupleO, Zip } from './util.ts'; /** * Given a dependency factory, returns a new factory that will always resolve diff --git a/lambda-ioc/deno/container.ts b/lambda-ioc/deno/container.ts index 28133be..2f19e2b 100644 --- a/lambda-ioc/deno/container.ts +++ b/lambda-ioc/deno/container.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ -import { ContainerKey, ContextualParamsToResolverKeys } from './util.ts'; +export type ContainerKey = string | symbol +type ConstrainedKey = Exclude type ExtractPrefix = S extends `${infer Prefix}:${string}` ? Prefix : never @@ -11,7 +12,56 @@ type ExtractPrefixedValues< BaseKeys extends keyof Struct = keyof Struct, > = BaseKeys extends `${Prefix}:${infer U}` ? Struct[`${Prefix}:${U}`] : never -type ConstrainedKey = Exclude +type KeysMatching = { + [K in keyof Collection]-?: Collection[K] extends Value ? K : never +}[keyof Collection] + +type ContextualParamsToSyncResolverKeys< + TSyncDependencies extends Record, + TAsyncDependencies extends Record, + TParams extends + | readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[] + | [], +> = { + [K in keyof TParams]: TParams[K] extends + | ReadableSyncContainer> + | ReadableAsyncContainer> + ? '$' + : KeysMatching +} + +type ContextualParamsToAsyncResolverKeys< + TSyncDependencies extends Record, + TAsyncDependencies extends Record, + TParams extends + | readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | TAsyncDependencies[keyof TAsyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[] + | [], +> = { + [K in keyof TParams]: TParams[K] extends + | ReadableSyncContainer> + | ReadableAsyncContainer> + ? '$' + : + | KeysMatching + | KeysMatching +} export interface SyncDependencyFactory< T, @@ -116,7 +166,9 @@ export interface WritableContainer< TAsyncDependencies extends Record, > { /** - * Register a new synchronous dependency factory. + * Registers a new synchronous dependency factory. + * It cannot be used when self-resolution is needed. Use + * `registerConstructor` instead. * * @param name The "name" of the dependency (can be a symbol). * @param dependency A dependency factory. @@ -148,7 +200,9 @@ export interface WritableContainer< > /** - * Register a new asynchronous dependency factory. + * Registers a new asynchronous dependency factory. + * It cannot be used when self-resolution is needed. Use + * `registerAsyncConstructor` instead. * * @param name The "name" of the dependency (can be a symbol). * @param dependency A dependency factory. @@ -179,10 +233,49 @@ export interface WritableContainer< } > + registerConstructor< + TName extends ConstrainedKey, + TParams extends readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[], + TClass extends TName extends '$' | keyof TAsyncDependencies + ? never + : TName extends keyof TSyncDependencies + ? TSyncDependencies[TName] + : unknown, + TDependencies extends ContextualParamsToSyncResolverKeys< + TSyncDependencies, + TAsyncDependencies, + TParams + >, + >( + name: TName, + constructor: new (...args: TParams) => TClass, + ...args: TDependencies + ): Container< + { + [TK in + | keyof TSyncDependencies + | TName]: TK extends keyof TSyncDependencies + ? TName extends TK + ? TClass + : TSyncDependencies[TK] + : TClass + }, + TAsyncDependencies + > + /** * Registers a new constructor that might have asynchronous-resolvable * dependencies. This method is helpful when the constructor combinator is - * not powerful enough (as it's only able to resolve synchronously). + * not powerful enough (as it's only able to resolve synchronously, and it + * cannot take advantage of self-resolution either). * * @param name The "name" of the dependency (can be a symbol). * @param constructor A class constructor, that will be use to resolve the @@ -196,13 +289,19 @@ export interface WritableContainer< TParams extends readonly ( | TSyncDependencies[keyof TSyncDependencies] | TAsyncDependencies[keyof TAsyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > )[], TClass extends TName extends '$' | keyof TSyncDependencies ? never : TName extends keyof TAsyncDependencies ? TAsyncDependencies[TName] : unknown, - TDependencies extends ContextualParamsToResolverKeys< + TDependencies extends ContextualParamsToAsyncResolverKeys< TSyncDependencies, TAsyncDependencies, TParams @@ -225,7 +324,7 @@ export interface WritableContainer< > /** - * Register an already instantiated dependency. + * Registers an already instantiated dependency. * * @param name The "name" of the dependency (can be a symbol). * @param dependency An already instantiated value. @@ -430,18 +529,77 @@ function __createContainer< } }, + registerConstructor< + TName extends ConstrainedKey, + TParams extends readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[], + TClass extends TName extends '$' | keyof TAsyncDependencies + ? never + : TName extends keyof TSyncDependencies + ? TSyncDependencies[TName] + : unknown, + TDependencies extends ContextualParamsToSyncResolverKeys< + TSyncDependencies, + TAsyncDependencies, + TParams + >, + >( + name: TName, + constructor: new (...args: TParams) => TClass, + ...args: TDependencies + ): ContainerWithNewSyncDep { + const factory = (container: typeof this) => { + const resolvedParams = args.map((arg) => { + return arg === '$' + ? this + : container.resolve(arg as keyof TSyncDependencies) + }) as unknown as TParams + + return new constructor(...resolvedParams) + } + + if (name in syncDependencies) { + return __createContainer( + { + ...syncDependencies, + [name]: factory, + }, + asyncDependencies, + ) as ContainerWithNewSyncDep + } else { + ;(syncDependencies as Record)[name] = factory + return __createContainer( + syncDependencies, + asyncDependencies, + ) as ContainerWithNewSyncDep + } + }, + registerAsyncConstructor< TName extends ConstrainedKey, TParams extends readonly ( | TSyncDependencies[keyof TSyncDependencies] | TAsyncDependencies[keyof TAsyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > )[], TClass extends TName extends '$' | keyof TSyncDependencies ? never : TName extends keyof TAsyncDependencies ? TAsyncDependencies[TName] : unknown, - TDependencies extends ContextualParamsToResolverKeys< + TDependencies extends ContextualParamsToAsyncResolverKeys< TSyncDependencies, TAsyncDependencies, TParams @@ -453,7 +611,9 @@ function __createContainer< ): ContainerWithNewAsyncDep { const factory = async (container: typeof this) => { const argPromises = args.map((arg) => { - return (arg as string) in syncDependencies + return arg === '$' + ? this + : (arg as string) in syncDependencies ? container.resolve(arg as keyof TSyncDependencies) : container.resolveAsync(arg as keyof TAsyncDependencies) }) diff --git a/lambda-ioc/deno/util.ts b/lambda-ioc/deno/util.ts index 719c9a5..91aff8e 100644 --- a/lambda-ioc/deno/util.ts +++ b/lambda-ioc/deno/util.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ -export type ContainerKey = string | symbol +import { ContainerKey } from './container.ts'; export type Zip = { [K in keyof A]: K extends keyof B ? [A[K], B[K]] : never @@ -11,25 +11,6 @@ export type ParamsToResolverKeys = { [K in keyof T]: ContainerKey } -export type ContextualParamsToResolverKeys< - TSyncDependencies extends Record, - TAsyncDependencies extends Record, - TParams extends - | readonly ( - | TSyncDependencies[keyof TSyncDependencies] - | TAsyncDependencies[keyof TAsyncDependencies] - )[] - | [], -> = { - [K in keyof TParams]: - | KeysMatching - | KeysMatching -} - -type KeysMatching = { - [K in keyof Collection]-?: Collection[K] extends Value ? K : never -}[keyof Collection] - export type MergeNoDuplicates = { [K in keyof A | keyof B]: K extends keyof B ? K extends keyof A diff --git a/lambda-ioc/src/__tests__/container.test.ts b/lambda-ioc/src/__tests__/container.test.ts index 706cdec..c4ec46c 100644 --- a/lambda-ioc/src/__tests__/container.test.ts +++ b/lambda-ioc/src/__tests__/container.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/ban-types */ -import { Container, createContainer } from '..' +import { Container, ReadableSyncContainer, createContainer } from '..' // Behavioural tests describe('container', () => { @@ -9,6 +9,68 @@ describe('container', () => { expect(resolvedContainer).toBe(container) }) + it('can do indirect self-resolution for registerConstructor', () => { + class DependsOnContainer { + public readonly theAnswer: number + + // We inject the container itself instead of the "final" resolved value. + // Here it makes no sense, but it can be useful when we need to inject + // factories. + constructor( + cc: ReadableSyncContainer<{ + theAnswerToEverything: number + }>, + ) { + this.theAnswer = cc.resolve('theAnswerToEverything') + } + } + + const container = createContainer() + .registerValue('theAnswerToEverything', 42) + .registerConstructor('C', DependsOnContainer, '$') + // This 2n part is to check for a type-related regression that appears + // when the container has more dependencies that the needed ones. + .registerValue('anotherOne', 25) + .registerConstructor('C2', DependsOnContainer, '$') + + const dependsOnContainer = container.resolve('C') + expect(dependsOnContainer.theAnswer).toBe(42) + + const dependsOnContainer2 = container.resolve('C2') + expect(dependsOnContainer2.theAnswer).toBe(42) + }) + + it('can do indirect self-resolution for registerAsyncConstructor', async () => { + class DependsOnContainer { + public readonly theAnswer: number + + // We inject the container itself instead of the "final" resolved value. + // Here it makes no sense, but it can be useful when we need to inject + // factories. + constructor( + public readonly cc: ReadableSyncContainer<{ + theAnswerToEverything: number + }>, + ) { + this.theAnswer = cc.resolve('theAnswerToEverything') + } + } + + const container = createContainer() + .registerValue('theAnswerToEverything', 42) + .registerAsyncConstructor('C', DependsOnContainer, '$') + // This 2n part is to check for a type-related regression that appears + // when the container has more dependencies that the needed ones. + .registerValue('anotherOne', 25) + .registerAsyncConstructor('C2', DependsOnContainer, '$') + + const dependsOnContainer = await container.resolveAsync('C') + expect(dependsOnContainer.theAnswer).toBe(42) + + const dependsOnContainer2 = await container.resolveAsync('C2') + expect(dependsOnContainer2.theAnswer).toBe(42) + }) + it('can register simple values', () => { const container = createContainer() .registerValue('foo', 'bar') @@ -126,6 +188,32 @@ describe('container', () => { expect(g2).toContain(60) }) + it('can register constructors without having to rely on combinators', () => { + class C { + constructor(public readonly a: number, public readonly b: string) {} + } + + const container = createContainer() + .registerValue('numeric', 10) + .registerValue('text', 'hello') + .registerConstructor('C', C, 'numeric', 'text') + + const c1 = container.resolve('C') + expect(c1.a).toBe(10) + expect(c1.b).toBe('hello') + + // Re-registering a new dependency with the same name should work, as long + // as we preserve its type. + const secondContainer = container + .registerValue('float', 34.5) + .registerValue('name', 'Bob') + .registerConstructor('C', C, 'float', 'name') + + const c2 = secondContainer.resolve('C') + expect(c2.a).toBe(34.5) + expect(c2.b).toBe('Bob') + }) + it('can register constructors with a mix of sync & async dependencies', async () => { class C { constructor(public readonly a: number, public readonly b: string) {} diff --git a/lambda-ioc/src/combinators.ts b/lambda-ioc/src/combinators.ts index 279caee..4d8a39b 100644 --- a/lambda-ioc/src/combinators.ts +++ b/lambda-ioc/src/combinators.ts @@ -1,10 +1,11 @@ import { AsyncDependencyFactory, + ContainerKey, ReadableContainer, ReadableSyncContainer, SyncDependencyFactory, } from './container' -import { ContainerKey, ParamsToResolverKeys, TupleO, Zip } from './util' +import { ParamsToResolverKeys, TupleO, Zip } from './util' /** * Given a dependency factory, returns a new factory that will always resolve diff --git a/lambda-ioc/src/container.ts b/lambda-ioc/src/container.ts index 165ec93..2f19e2b 100644 --- a/lambda-ioc/src/container.ts +++ b/lambda-ioc/src/container.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ -import { ContainerKey, ContextualParamsToResolverKeys } from './util' +export type ContainerKey = string | symbol +type ConstrainedKey = Exclude type ExtractPrefix = S extends `${infer Prefix}:${string}` ? Prefix : never @@ -11,7 +12,56 @@ type ExtractPrefixedValues< BaseKeys extends keyof Struct = keyof Struct, > = BaseKeys extends `${Prefix}:${infer U}` ? Struct[`${Prefix}:${U}`] : never -type ConstrainedKey = Exclude +type KeysMatching = { + [K in keyof Collection]-?: Collection[K] extends Value ? K : never +}[keyof Collection] + +type ContextualParamsToSyncResolverKeys< + TSyncDependencies extends Record, + TAsyncDependencies extends Record, + TParams extends + | readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[] + | [], +> = { + [K in keyof TParams]: TParams[K] extends + | ReadableSyncContainer> + | ReadableAsyncContainer> + ? '$' + : KeysMatching +} + +type ContextualParamsToAsyncResolverKeys< + TSyncDependencies extends Record, + TAsyncDependencies extends Record, + TParams extends + | readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | TAsyncDependencies[keyof TAsyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[] + | [], +> = { + [K in keyof TParams]: TParams[K] extends + | ReadableSyncContainer> + | ReadableAsyncContainer> + ? '$' + : + | KeysMatching + | KeysMatching +} export interface SyncDependencyFactory< T, @@ -116,7 +166,9 @@ export interface WritableContainer< TAsyncDependencies extends Record, > { /** - * Register a new synchronous dependency factory. + * Registers a new synchronous dependency factory. + * It cannot be used when self-resolution is needed. Use + * `registerConstructor` instead. * * @param name The "name" of the dependency (can be a symbol). * @param dependency A dependency factory. @@ -148,7 +200,9 @@ export interface WritableContainer< > /** - * Register a new asynchronous dependency factory. + * Registers a new asynchronous dependency factory. + * It cannot be used when self-resolution is needed. Use + * `registerAsyncConstructor` instead. * * @param name The "name" of the dependency (can be a symbol). * @param dependency A dependency factory. @@ -179,10 +233,49 @@ export interface WritableContainer< } > + registerConstructor< + TName extends ConstrainedKey, + TParams extends readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[], + TClass extends TName extends '$' | keyof TAsyncDependencies + ? never + : TName extends keyof TSyncDependencies + ? TSyncDependencies[TName] + : unknown, + TDependencies extends ContextualParamsToSyncResolverKeys< + TSyncDependencies, + TAsyncDependencies, + TParams + >, + >( + name: TName, + constructor: new (...args: TParams) => TClass, + ...args: TDependencies + ): Container< + { + [TK in + | keyof TSyncDependencies + | TName]: TK extends keyof TSyncDependencies + ? TName extends TK + ? TClass + : TSyncDependencies[TK] + : TClass + }, + TAsyncDependencies + > + /** * Registers a new constructor that might have asynchronous-resolvable * dependencies. This method is helpful when the constructor combinator is - * not powerful enough (as it's only able to resolve synchronously). + * not powerful enough (as it's only able to resolve synchronously, and it + * cannot take advantage of self-resolution either). * * @param name The "name" of the dependency (can be a symbol). * @param constructor A class constructor, that will be use to resolve the @@ -196,13 +289,19 @@ export interface WritableContainer< TParams extends readonly ( | TSyncDependencies[keyof TSyncDependencies] | TAsyncDependencies[keyof TAsyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > )[], TClass extends TName extends '$' | keyof TSyncDependencies ? never : TName extends keyof TAsyncDependencies ? TAsyncDependencies[TName] : unknown, - TDependencies extends ContextualParamsToResolverKeys< + TDependencies extends ContextualParamsToAsyncResolverKeys< TSyncDependencies, TAsyncDependencies, TParams @@ -225,7 +324,7 @@ export interface WritableContainer< > /** - * Register an already instantiated dependency. + * Registers an already instantiated dependency. * * @param name The "name" of the dependency (can be a symbol). * @param dependency An already instantiated value. @@ -430,18 +529,77 @@ function __createContainer< } }, + registerConstructor< + TName extends ConstrainedKey, + TParams extends readonly ( + | TSyncDependencies[keyof TSyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > + )[], + TClass extends TName extends '$' | keyof TAsyncDependencies + ? never + : TName extends keyof TSyncDependencies + ? TSyncDependencies[TName] + : unknown, + TDependencies extends ContextualParamsToSyncResolverKeys< + TSyncDependencies, + TAsyncDependencies, + TParams + >, + >( + name: TName, + constructor: new (...args: TParams) => TClass, + ...args: TDependencies + ): ContainerWithNewSyncDep { + const factory = (container: typeof this) => { + const resolvedParams = args.map((arg) => { + return arg === '$' + ? this + : container.resolve(arg as keyof TSyncDependencies) + }) as unknown as TParams + + return new constructor(...resolvedParams) + } + + if (name in syncDependencies) { + return __createContainer( + { + ...syncDependencies, + [name]: factory, + }, + asyncDependencies, + ) as ContainerWithNewSyncDep + } else { + ;(syncDependencies as Record)[name] = factory + return __createContainer( + syncDependencies, + asyncDependencies, + ) as ContainerWithNewSyncDep + } + }, + registerAsyncConstructor< TName extends ConstrainedKey, TParams extends readonly ( | TSyncDependencies[keyof TSyncDependencies] | TAsyncDependencies[keyof TAsyncDependencies] + | ReadableSyncContainer> + | ReadableAsyncContainer> + | ReadableContainer< + Partial, + Partial + > )[], TClass extends TName extends '$' | keyof TSyncDependencies ? never : TName extends keyof TAsyncDependencies ? TAsyncDependencies[TName] : unknown, - TDependencies extends ContextualParamsToResolverKeys< + TDependencies extends ContextualParamsToAsyncResolverKeys< TSyncDependencies, TAsyncDependencies, TParams @@ -453,7 +611,9 @@ function __createContainer< ): ContainerWithNewAsyncDep { const factory = async (container: typeof this) => { const argPromises = args.map((arg) => { - return (arg as string) in syncDependencies + return arg === '$' + ? this + : (arg as string) in syncDependencies ? container.resolve(arg as keyof TSyncDependencies) : container.resolveAsync(arg as keyof TAsyncDependencies) }) diff --git a/lambda-ioc/src/util.ts b/lambda-ioc/src/util.ts index 719c9a5..de1fdb3 100644 --- a/lambda-ioc/src/util.ts +++ b/lambda-ioc/src/util.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ -export type ContainerKey = string | symbol +import { ContainerKey } from './container' export type Zip = { [K in keyof A]: K extends keyof B ? [A[K], B[K]] : never @@ -11,25 +11,6 @@ export type ParamsToResolverKeys = { [K in keyof T]: ContainerKey } -export type ContextualParamsToResolverKeys< - TSyncDependencies extends Record, - TAsyncDependencies extends Record, - TParams extends - | readonly ( - | TSyncDependencies[keyof TSyncDependencies] - | TAsyncDependencies[keyof TAsyncDependencies] - )[] - | [], -> = { - [K in keyof TParams]: - | KeysMatching - | KeysMatching -} - -type KeysMatching = { - [K in keyof Collection]-?: Collection[K] extends Value ? K : never -}[keyof Collection] - export type MergeNoDuplicates = { [K in keyof A | keyof B]: K extends keyof B ? K extends keyof A From 12bfd60c8d4c0970098d0962814c32d971c55f62 Mon Sep 17 00:00:00 2001 From: Andres Correa Casablanca Date: Sun, 20 Feb 2022 14:11:56 +0100 Subject: [PATCH 3/4] chore: bump version to 0.7.0 --- lambda-ioc/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-ioc/package.json b/lambda-ioc/package.json index d1cb467..31eead9 100644 --- a/lambda-ioc/package.json +++ b/lambda-ioc/package.json @@ -1,6 +1,6 @@ { "name": "@coderspirit/lambda-ioc", - "version": "0.6.0", + "version": "0.7.0", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist/cjs/index.d.ts", From 11b34fa9e6c1903d46cabf30258e54cf5a820867 Mon Sep 17 00:00:00 2001 From: Andres Correa Casablanca Date: Sun, 20 Feb 2022 14:18:08 +0100 Subject: [PATCH 4/4] docs: explain indirect-resolution gotchas --- README.md | 8 ++++++++ lambda-ioc/README.md | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index b710f96..ab4e57c 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,14 @@ container.resolveGroup('group2') // ~ [3, 4], not necessarily in the same order // up to date. This is useful if we want to use the container as a factory for // some of your dependencies. const resolvedContainer = container.resolve('$') + +// If you want to indirectly resolve the container itself, it can be done only +// with the methods: +// - resolveConstructor +// - resolveAsyncConstructor +// This is because they have "privileged" information about the container's +// type, while relying on `register` or `registerAsync` plus "combinators" does +// not allow us to leverage that information. ``` It is also possible to register and resolve asynchronous factories and diff --git a/lambda-ioc/README.md b/lambda-ioc/README.md index b710f96..ab4e57c 100644 --- a/lambda-ioc/README.md +++ b/lambda-ioc/README.md @@ -85,6 +85,14 @@ container.resolveGroup('group2') // ~ [3, 4], not necessarily in the same order // up to date. This is useful if we want to use the container as a factory for // some of your dependencies. const resolvedContainer = container.resolve('$') + +// If you want to indirectly resolve the container itself, it can be done only +// with the methods: +// - resolveConstructor +// - resolveAsyncConstructor +// This is because they have "privileged" information about the container's +// type, while relying on `register` or `registerAsync` plus "combinators" does +// not allow us to leverage that information. ``` It is also possible to register and resolve asynchronous factories and