Skip to content

Commit

Permalink
feat: Permit unoverride of injectionToken with exactly one implementa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
Iku-turso committed May 8, 2023
1 parent b0d3d64 commit 4346c92
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 7 deletions.
10 changes: 7 additions & 3 deletions packages/injectable/core/index.d.ts
Expand Up @@ -19,7 +19,7 @@ export interface DiContainer extends DiContainerForInjection {
instantiateStub: Instantiate<InjectionInstance, InstantiationParam>,
): void;

unoverride(injectable: Injectable<any, any, any>): void;
unoverride(alias: InjectionToken<any, any> | Injectable<any, any, any>): void;

register(...injectables: Injectable<any, any, any>[]): void;

Expand Down Expand Up @@ -349,8 +349,12 @@ export const instantiationDecoratorToken: InjectionToken<

export const registrationCallbackToken: RegistrationCallback;
export const deregistrationCallbackToken: RegistrationCallback;
export const isInjectable: (thing: unknown) => thing is Injectable<unknown, unknown, unknown>;
export const isInjectionToken: (thing: unknown) => thing is InjectionToken<unknown, unknown>;
export const isInjectable: (
thing: unknown,
) => thing is Injectable<unknown, unknown, unknown>;
export const isInjectionToken: (
thing: unknown,
) => thing is InjectionToken<unknown, unknown>;

export function createContainer(
containerId: string,
Expand Down
25 changes: 24 additions & 1 deletion packages/injectable/core/index.test-d.ts
Expand Up @@ -47,7 +47,7 @@ const decoratorForToken = getInjectable({
injectionToken: instantiationDecoratorToken,
});

const foo: unknown = "number";
const foo: unknown = 'number';

if (isInjectable(foo)) {
expectType<Injectable<unknown, unknown, unknown>>(foo);
Expand Down Expand Up @@ -459,3 +459,26 @@ expectError(
}),
),
);

// Overrides and unoverrides
const someStringInjectionToken = getInjectionToken<string>({
id: 'irrelevant',
});

const someInjectable = getInjectable({
id: 'some-injectable',
instantiate: di => 'some-string',
injectionToken: someStringInjectionToken,
});

// given injectable, when overridden using injectable, typing is ok.
di.override(someInjectable, () => 'some-other-string');

// given injectable, when overridden using injectionToken, typing is ok.
di.override(someStringInjectionToken, () => 'some-other-string');

// given injectable, when unoverridden using injectable, typing is ok.
di.unoverride(someInjectable);

// given injectable, when unoverridden using injectionToken, typing is ok.
di.unoverride(someStringInjectionToken);
Expand Up @@ -151,7 +151,10 @@ export default (containerId, { detectCycles = true } = {}) => {
overridingInjectables,
});

const unoverride = unoverrideFor({ overridingInjectables });
const unoverride = unoverrideFor({
overridingInjectables,
getRelatedInjectables,
});

const decorateFunction = decorateFunctionFor({ decorate });

Expand Down
Expand Up @@ -42,7 +42,21 @@ export const overrideFor =
};

export const unoverrideFor =
({ overridingInjectables }) =>
injectable => {
({ overridingInjectables, getRelatedInjectables }) =>
alias => {
const [injectable] = getRelatedInjectables(alias);

if (!injectable) {
throw new Error(
`Tried to unoverride "${alias.id}", but it was not registered.`,
);
}

if (!overridingInjectables.has(injectable)) {
throw new Error(
`Tried to unoverride "${alias.id}", but it was not overridden.`,
);
}

overridingInjectables.delete(injectable);
};
72 changes: 72 additions & 0 deletions packages/injectable/core/src/scenarios/override.test.js
Expand Up @@ -146,6 +146,78 @@ describe('createContainer.override', () => {
expect(actual).toBe('some-original-value');
});

it('given an injectable with injection token is overridden, but then unoverriden using injectionToken, injects the original injectable', () => {
const someInjectionToken = getInjectionToken('irrelevant');

const someInjectable = getInjectable({
id: 'irrelevant',
instantiate: () => 'some-original-value',
injectionToken: someInjectionToken,
});

const di = createContainer('some-container');

di.register(someInjectable);

di.override(someInjectionToken, () => 'irrelevant');

di.unoverride(someInjectionToken);

const actual = di.inject(someInjectable);

expect(actual).toBe('some-original-value');
});

it('given an injectable with injection token is overridden, but then unoverridden using injectable, injects the original injectable', () => {
const someInjectionToken = getInjectionToken('irrelevant');

const someInjectable = getInjectable({
id: 'irrelevant',
instantiate: () => 'some-original-value',
injectionToken: someInjectionToken,
});

const di = createContainer('some-container');

di.register(someInjectable);

di.override(someInjectionToken, () => 'irrelevant');

di.unoverride(someInjectable);

const actual = di.inject(someInjectable);

expect(actual).toBe('some-original-value');
});

it('given an injectable, but not overridden, when unoverridden, throws', () => {
const someInjectable = getInjectable({
id: 'irrelevant',
instantiate: () => 'some-original-value',
});

const di = createContainer('some-container');

di.register(someInjectable);

expect(() => {
di.unoverride(someInjectable);
}).toThrow('Tried to unoverride "irrelevant", but it was not overridden.');
});

it('given an injectable, but not registered, when unoverridden, throws', () => {
const someInjectable = getInjectable({
id: 'irrelevant',
instantiate: () => 'some-original-value',
});

const di = createContainer('some-container');

expect(() => {
di.unoverride(someInjectable);
}).toThrow('Tried to unoverride "irrelevant", but it was not registered.');
});

it('when overriding non-registered injectable, throws', () => {
const di = createContainer('some-container');

Expand Down

0 comments on commit 4346c92

Please sign in to comment.