Skip to content

Commit

Permalink
Update container with try get methods (#1674)
Browse files Browse the repository at this point in the history
* feat: update container with try get methods

* docs: update changelog

* docs: update api docs
  • Loading branch information
notaphplover authored Dec 5, 2024
1 parent fdbf854 commit ee442fb
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 7 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- Updated `interfaces.NextArgs` with optional `isOptional` param.
- Updated `container` with `tryGet`.
- Updated `container` with `tryGetAsync`.
- Updated `container` with `tryGetTagged`.
- Updated `container` with `tryGetTaggedAsync`.
- Updated `container` with `tryGetNamed`.
- Updated `container` with `tryGetNamedAsync`.
- Updated `container` with `tryGetAll`.
- Updated `container` with `tryGetAllAsync`.
- Updated `container` with `tryGetAllTagged`.
- Updated `container` with `tryGetAllTaggedAsync`.
- Updated `container` with `tryGetAllNamed`.
- Updated `container` with `tryGetAllNamedAsync`.

### Fixed

Expand Down
194 changes: 188 additions & 6 deletions src/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,15 +390,23 @@ class Container implements interfaces.Container {
// The runtime identifier must be associated with only one binding
// use getAll when the runtime identifier is associated with multiple bindings
public get<T>(serviceIdentifier: interfaces.ServiceIdentifier<T>): T {
const getArgs: GetArgs<T> = this._getNotAllArgs(serviceIdentifier, false);
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
false,
);

return this._getButThrowIfAsync<T>(getArgs) as T;
}

public async getAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
): Promise<T> {
const getArgs: GetArgs<T> = this._getNotAllArgs(serviceIdentifier, false);
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
false,
);

return this._get<T>(getArgs) as Promise<T> | T;
}
Expand All @@ -411,6 +419,7 @@ class Container implements interfaces.Container {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
false,
key,
value,
);
Expand All @@ -426,6 +435,7 @@ class Container implements interfaces.Container {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
false,
key,
value,
);
Expand Down Expand Up @@ -457,7 +467,11 @@ class Container implements interfaces.Container {
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options?: interfaces.GetAllOptions,
): T[] {
const getArgs: GetArgs<T> = this._getAllArgs(serviceIdentifier, options);
const getArgs: GetArgs<T> = this._getAllArgs(
serviceIdentifier,
options,
false,
);

return this._getButThrowIfAsync<T>(getArgs) as T[];
}
Expand All @@ -466,7 +480,11 @@ class Container implements interfaces.Container {
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options?: interfaces.GetAllOptions,
): Promise<T[]> {
const getArgs: GetArgs<T> = this._getAllArgs(serviceIdentifier, options);
const getArgs: GetArgs<T> = this._getAllArgs(
serviceIdentifier,
options,
false,
);

return this._getAll(getArgs);
}
Expand All @@ -479,6 +497,7 @@ class Container implements interfaces.Container {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
true,
false,
key,
value,
);
Expand All @@ -494,6 +513,7 @@ class Container implements interfaces.Container {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
true,
false,
key,
value,
);
Expand Down Expand Up @@ -535,6 +555,164 @@ class Container implements interfaces.Container {
return resolved;
}

public tryGet<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
): T | undefined {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
true,
);

return this._getButThrowIfAsync<T>(getArgs) as T | undefined;
}

public async tryGetAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
): Promise<T | undefined> {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
true,
);

return this._get<T>(getArgs) as Promise<T> | T | undefined;
}

public tryGetTagged<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): T | undefined {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
true,
key,
value,
);

return this._getButThrowIfAsync<T>(getArgs) as T | undefined;
}

public async tryGetTaggedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): Promise<T | undefined> {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
false,
true,
key,
value,
);

return this._get<T>(getArgs) as Promise<T> | T | undefined;
}

public tryGetNamed<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): T | undefined {
return this.tryGetTagged<T>(
serviceIdentifier,
METADATA_KEY.NAMED_TAG,
named,
);
}

public async tryGetNamedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): Promise<T | undefined> {
return this.tryGetTaggedAsync<T>(
serviceIdentifier,
METADATA_KEY.NAMED_TAG,
named,
);
}

public tryGetAll<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options?: interfaces.GetAllOptions,
): T[] {
const getArgs: GetArgs<T> = this._getAllArgs(
serviceIdentifier,
options,
true,
);

return this._getButThrowIfAsync<T>(getArgs) as T[];
}

public async tryGetAllAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options?: interfaces.GetAllOptions,
): Promise<T[]> {
const getArgs: GetArgs<T> = this._getAllArgs(
serviceIdentifier,
options,
true,
);

return this._getAll(getArgs);
}

public tryGetAllTagged<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): T[] {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
true,
true,
key,
value,
);

return this._getButThrowIfAsync<T>(getArgs) as T[];
}

public async tryGetAllTaggedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): Promise<T[]> {
const getArgs: GetArgs<T> = this._getNotAllArgs(
serviceIdentifier,
true,
true,
key,
value,
);

return this._getAll(getArgs);
}

public tryGetAllNamed<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): T[] {
return this.tryGetAllTagged<T>(
serviceIdentifier,
METADATA_KEY.NAMED_TAG,
named,
);
}

public async tryGetAllNamedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): Promise<T[]> {
return this.tryGetAllTaggedAsync<T>(
serviceIdentifier,
METADATA_KEY.NAMED_TAG,
named,
);
}

private _preDestroy(
constructor: NewableFunction | undefined,
instance: unknown,
Expand Down Expand Up @@ -828,23 +1006,25 @@ class Container implements interfaces.Container {
return this._planAndResolve<T>()(planAndResolveArgs);
}

private _getButThrowIfAsync<T>(getArgs: GetArgs<T>): T | T[] {
private _getButThrowIfAsync<T>(getArgs: GetArgs<T>): undefined | T | T[] {
const result: interfaces.ContainerResolution<T> = this._get<T>(getArgs);

if (isPromiseOrContainsPromise<T>(result)) {
throw new Error(ERROR_MSGS.LAZY_IN_SYNC(getArgs.serviceIdentifier));
}

return result as T | T[];
return result as undefined | T | T[];
}

private _getAllArgs<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options: interfaces.GetAllOptions | undefined,
isOptional: boolean,
): GetArgs<T> {
const getAllArgs: GetArgs<T> = {
avoidConstraints: !(options?.enforceBindingConstraints ?? false),
isMultiInject: true,
isOptional,
serviceIdentifier,
};

Expand All @@ -854,12 +1034,14 @@ class Container implements interfaces.Container {
private _getNotAllArgs<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
isMultiInject: boolean,
isOptional: boolean,
key?: string | number | symbol | undefined,
value?: unknown,
): GetArgs<T> {
const getNotAllArgs: GetArgs<T> = {
avoidConstraints: false,
isMultiInject,
isOptional,
key,
serviceIdentifier,
value,
Expand Down
56 changes: 55 additions & 1 deletion src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace interfaces {
export type DynamicValue<T> = (context: interfaces.Context) => T | Promise<T>;
export type ContainerResolution<T> = T | Promise<T> | (T | Promise<T>)[];
export type ContainerResolution<T> =
| undefined
| T
| Promise<T>
| (T | Promise<T>)[];

type AsyncCallback<TCallback> = TCallback extends (
...args: infer TArgs
Expand Down Expand Up @@ -301,6 +305,56 @@ namespace interfaces {
onDeactivation: BindingDeactivation<T>,
): void;
resolve<T>(constructorFunction: interfaces.Newable<T>): T;
tryGet<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
): T | undefined;
tryGetAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
): Promise<T | undefined>;
tryGetTagged<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): T | undefined;
tryGetTaggedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): Promise<T | undefined>;
tryGetNamed<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): T | undefined;
tryGetNamedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): Promise<T | undefined>;
tryGetAll<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options?: interfaces.GetAllOptions,
): T[];
tryGetAllAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
options?: interfaces.GetAllOptions,
): Promise<T[]>;
tryGetAllTagged<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): T[];
tryGetAllTaggedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
key: string | number | symbol,
value: unknown,
): Promise<T[]>;
tryGetAllNamed<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): T[];
tryGetAllNamedAsync<T>(
serviceIdentifier: interfaces.ServiceIdentifier<T>,
named: string | number | symbol,
): Promise<T[]>;
load(...modules: ContainerModule[]): void;
loadAsync(...modules: AsyncContainerModule[]): Promise<void>;
unload(...modules: ContainerModuleBase[]): void;
Expand Down
Loading

0 comments on commit ee442fb

Please sign in to comment.