Skip to content

Commit

Permalink
Merge pull request #173 from alecgibson/fix-strong-generic
Browse files Browse the repository at this point in the history
🏷️ fix(strongly-typed) allow use of generic helpers
  • Loading branch information
notaphplover authored Dec 10, 2024
2 parents 04094c0 + 1564a20 commit c5b382b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-lobsters-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inversifyjs/strongly-typed": patch
---

Fixed types to allow use of generic helper functions
34 changes: 30 additions & 4 deletions packages/container/libraries/strongly-typed/src/container.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,13 @@ describe('interfaces', () => {
});

it('gets a promise', async () => {
// @ts-expect-error :: can't call get() to get Promise
expect(() => container.get('asyncNumber')).toThrow(
'it has asynchronous dependencies',
);
expect(() => {
const num = container.get('asyncNumber');
/* eslint-disable @typescript-eslint/no-unused-expressions */
// @ts-expect-error :: num is never
num.then;
/* eslint-enable @typescript-eslint/no-unused-expressions */
}).toThrow('it has asynchronous dependencies');
const n: Promise<number> = container.getAsync('asyncNumber');
expect(await n).toBe(1);
});
Expand Down Expand Up @@ -201,6 +204,29 @@ describe('interfaces', () => {
container.bind('foo').to(Bar);
});
});

describe('generics', () => {
beforeEach(() => {
container.bind('foo').to(Foo);
});

it('can be used in a generic function', () => {
function test<T extends BindingMap>(
container: TypedContainer<T>,
): void {
const foo: Foo = container.get('foo');
// @ts-expect-error :: can't assign Foo to Bar
const bar: Bar = container.get('foo');

// eslint-disable-next-line @typescript-eslint/no-unused-expressions
foo;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
bar;
}

test(container);
});
});
});
});
});
44 changes: 20 additions & 24 deletions packages/container/libraries/strongly-typed/src/container.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */

import { Container, type interfaces } from 'inversify';

type IfAny<T, TYes, TNo> = 0 extends 1 & T ? TYes : TNo;
Expand All @@ -23,11 +23,7 @@ type ContainerBinding<
? any
: never;

type Synchronous<T extends BindingMap> = IfAny<
T,
any,
{ [K in keyof T as T[K] extends Promise<any> ? never : K]: T[K] }
>;
type NeverPromise<T> = T extends Promise<any> ? never : T;

type First<T extends any[]> = T extends [infer TFirst, ...any[]]
? TFirst
Expand All @@ -44,47 +40,47 @@ interface ContainerOverrides<
parent: ContainerOverrides<First<TAncestors>, AllButFirst<TAncestors>> | null;
bind: Bind<T>;
get: <
TBound extends ContainerBinding<Synchronous<T>, TKey>,
TKey extends MappedServiceIdentifier<Synchronous<T>> = any,
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
>(
serviceIdentifier: TKey,
) => TBound;
) => NeverPromise<TBound>;
getNamed: <
TBound extends ContainerBinding<Synchronous<T>, TKey>,
TKey extends MappedServiceIdentifier<Synchronous<T>> = any,
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
>(
serviceIdentifier: TKey,
named: PropertyKey,
) => TBound;
) => NeverPromise<TBound>;
getTagged: <
TBound extends ContainerBinding<Synchronous<T>, TKey>,
TKey extends MappedServiceIdentifier<Synchronous<T>> = any,
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
>(
serviceIdentifier: TKey,
key: PropertyKey,
value: unknown,
) => TBound;
) => NeverPromise<TBound>;
getAll: <
TBound extends ContainerBinding<Synchronous<T>, TKey>,
TKey extends MappedServiceIdentifier<Synchronous<T>> = any,
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
>(
serviceIdentifier: TKey,
) => TBound[];
) => NeverPromise<TBound[]>;
getAllTagged: <
TBound extends ContainerBinding<Synchronous<T>, TKey>,
TKey extends MappedServiceIdentifier<Synchronous<T>> = any,
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
>(
serviceIdentifier: TKey,
key: PropertyKey,
value: unknown,
) => TBound[];
) => NeverPromise<TBound[]>;
getAllNamed: <
TBound extends ContainerBinding<Synchronous<T>, TKey>,
TKey extends MappedServiceIdentifier<Synchronous<T>> = any,
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
>(
serviceIdentifier: TKey,
named: PropertyKey,
) => TBound[];
) => NeverPromise<TBound[]>;
getAsync: <
TBound extends ContainerBinding<T, TKey>,
TKey extends MappedServiceIdentifier<T> = any,
Expand Down

0 comments on commit c5b382b

Please sign in to comment.