diff --git a/.changeset/metal-windows-run.md b/.changeset/metal-windows-run.md new file mode 100644 index 00000000..b77ba3bb --- /dev/null +++ b/.changeset/metal-windows-run.md @@ -0,0 +1,18 @@ +--- +'@freshgum/typedi': minor +--- + +The `ESService` decorator now supports classes with static properties. +This was an oversight in the original design of the decorator's signature. + +This change affects the type parameters consumed by `ESService`; the main difference +being that there is now a second parameter, `TClass`, which directly pertains to the +type of the class being decorated (and thus, the type returned by the decorator.) + +Therefore, the type parameters of the function have changed from [`T = unknown`][esservice-old-type-params] +to [`TInstance = unknown, TClass extends Constructable = Constructable`][esservice-new-type-params]. + +Current code which relies on the type signature of `ESService` will not be affected. + +[esservice-old-type-params]: https://github.com/freshgum-bubbles/typedi/blob/4c76133d3a94e119d5b4d44846213df42d3010a5/src/contrib/es/es-service.decorator.mts#L38 +[esservice-new-type-params]: https://github.com/freshgum-bubbles/typedi/blob/a3825b77fadf6143f282e5cf4b68c084076b8369/src/contrib/es/es-service.decorator.mts#L38 diff --git a/src/contrib/es/es-service.decorator.mts b/src/contrib/es/es-service.decorator.mts index 32fd534a..26d10230 100644 --- a/src/contrib/es/es-service.decorator.mts +++ b/src/contrib/es/es-service.decorator.mts @@ -35,7 +35,9 @@ import { ESClassDecorator } from '../util/es-class-decorator.type.mjs'; * * @returns A decorator which is then used upon a class. */ -export function ESService(dependencies: AnyServiceDependency[]): ESClassDecorator>; +export function ESService = Constructable>( + dependencies: AnyServiceDependency[] +): ESClassDecorator; /** * Marks class as a service that can be injected using Container. @@ -74,10 +76,10 @@ export function ESService(dependencies: AnyServiceDependency[]): ES * * @returns A decorator which is then used upon a class. */ -export function ESService( - options: Omit, 'dependencies'>, +export function ESService = Constructable>( + options: Omit, 'dependencies'>, dependencies: AnyServiceDependency[] -): ESClassDecorator>; +): ESClassDecorator; /** * Marks class as a service that can be injected using Container. @@ -115,22 +117,25 @@ export function ESService( * * @returns A decorator which is then used upon a class. */ -export function ESService( - options: ServiceOptionsWithDependencies> -): ESClassDecorator>; +export function ESService = Constructable>( + options: ServiceOptionsWithDependencies +): ESClassDecorator; -export function ESService( - optionsOrDependencies: Omit, 'dependencies'> | ServiceOptions | AnyServiceDependency[], +export function ESService = Constructable>( + optionsOrDependencies: + | Omit, 'dependencies'> + | ServiceOptions + | AnyServiceDependency[], maybeDependencies?: AnyServiceDependency[] -): ESClassDecorator> { - return (target: Constructable, context: ClassDecoratorContext) => { +): ESClassDecorator { + return (target: TClass, context: ClassDecoratorContext) => { // This is probably overcautious. if (context.kind !== 'class') { throw new Error('@ESService() must only be used to decorate classes.'); } Service( - optionsOrDependencies as ServiceOptionsWithoutDependencies, + optionsOrDependencies as ServiceOptionsWithoutDependencies, maybeDependencies as AnyServiceDependency[] )(target); }; diff --git a/src/contrib/upstream/tree b/src/contrib/upstream/tree index 9bab80dd..0edde7a5 160000 --- a/src/contrib/upstream/tree +++ b/src/contrib/upstream/tree @@ -1 +1 @@ -Subproject commit 9bab80dd2074490fd18b1d69bf179d66ccd84573 +Subproject commit 0edde7a5b9869d97cb5cfb50da6ab89312401cda diff --git a/test/contrib/es/es-service.spec.ts b/test/contrib/es/es-service.spec.ts index b661f30f..0cb7c59e 100644 --- a/test/contrib/es/es-service.spec.ts +++ b/test/contrib/es/es-service.spec.ts @@ -18,4 +18,22 @@ describe('ESService', () => { const context = createFakeClassDecoratorContext(MyService); expect(() => ESService([])(class {}, context)).not.toThrow(); }); + + it('should support static properties and methods', function () { + class ClassWithStaticMethodsAndProperties { + static property = true; + + static get getter() { + return 'value'; + } + + static method() {} + } + + const context = createFakeClassDecoratorContext(ClassWithStaticMethodsAndProperties); + + // We don't need an expect(...) here as this is a typing issue, not a runtime one. + ESService([])(ClassWithStaticMethodsAndProperties, context); + expect(true).toBe(true); + }); }); diff --git a/test/decorators/Service.spec.ts b/test/decorators/Service.spec.ts index a330ed76..e52855b7 100644 --- a/test/decorators/Service.spec.ts +++ b/test/decorators/Service.spec.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Container, ContainerInstance, Service, Token } from 'internal:typedi'; import { WrappedESServiceDecorator } from '../contrib/es/test-utils/es-service-decorator-wrapper.util'; +import { ESService } from 'internal:typedi/contrib/es/es-service.decorator.mjs'; // To ensure conformance between different Service implementations, // we wrap some decorators here with stubs which pass them quasi-values.