From 936ca711f4a3806baf0420692639ac9dcf09f386 Mon Sep 17 00:00:00 2001 From: freshgum Date: Sat, 9 Sep 2023 01:10:11 +0100 Subject: [PATCH] style(typedservice): format with Prettier --- src/types/typed-dependencies.type.ts | 302 ++++++++++---------- test/decorators/Service.spec.ts | 2 +- test/features/contrib/typed-service.spec.ts | 4 +- 3 files changed, 152 insertions(+), 156 deletions(-) diff --git a/src/types/typed-dependencies.type.ts b/src/types/typed-dependencies.type.ts index edae5376..d38aced9 100644 --- a/src/types/typed-dependencies.type.ts +++ b/src/types/typed-dependencies.type.ts @@ -1,122 +1,121 @@ - import { AnyServiceDependency, DependencyPairWithConfiguration } from '../interfaces/service-dependency.interface'; +import { AnyServiceDependency, DependencyPairWithConfiguration } from '../interfaces/service-dependency.interface'; import { Token } from '../token.class'; - import { LazyReference } from './lazy-reference.type'; - import { ServiceIdentifier } from './service-identifier.type'; - - /** - * @fileoverview @ignore - * The premise of this file is to allow for the conversion of service dependency lists into types which are appropriate - * for consuming as part of the service's constructor. - * - * The main focus of the implementation is below, in the `UnpackServiceDependency` type. This type transforms - * dependencies into their actual values. - * - * For a constructor passed into `ServiceSubject`, an "expected type" is returned. - * - * Consider the following example: - * ```ts - * @Service([AnotherService]) class MyService {} - * ``` - * - * In this case, as the `Service` overloads statically determines the class' dependencies, - * it's able to create a type which it *expects* the constructor to be. - * Considering the above example, the overload would expect the constructor it's going to - * operate on to be of type `new (arg1: AnotherService) => unknown`. - * - * If the type doesn't match (so say the constructor implementation expects another - * type in that dependency's place), the code won't compile without using `any`. - * - * Therefore, the following (thankfully) wouldn't compile: - * ```ts - * @Service([AnotherService]) - * class MyService { - * constructor (private value: number) { } - * } - * ``` - * - * If you look below, we also guard against built-in types surfacing into type design. For instance, if a class uses - * Number as a dependency, the accompanying argument in the constructor would be `number` instead of the discouraged - * `Number` type. - * - * Consider the following example: - * ```ts - * @Service({ factory: () => new Car(2) }, [Number]) - * class Car { - * constructor (private modelNumber: number) { } - * } - * ``` - * - * Instead of using `Number` in the constructor, we're able to use `number` in its place. This is because, for each - * dependency, we check if the type matches any of the built-in constructors, such as Number, String and Object. If they - * do, they're cast to their underlying native types. In the case of Number such as in the above example, that type is - * cast to `number`. The type compiler below is specially designed to accommodate this use-case. - * - * --- - * - * One note is that in the implementation, the expected type's return value is `unknown`. This is fine, as the return - * types of decorators doesn't actually matter to TypeScript. This makes the implementation a little bit simpler. - * - * The advantage of this overall approach is that it mostly keeps the type-checking decoupled from the Service decorator - * implementation. The actual type-checking is performed in this file. Furthermore, we get type-checking basically for - * free; this doesn't impact the size of the final bundle at all. Sweet! - * - * One notable drawback of this implementation is that, without modifications to TypeScript, we've absolutely no way to - * force `Optional`-marked parameters to possibly expect a null type. In the case of our resolution constraint bitmask, - * it's cast to a number internally. We can't check individual flags against it. - */ - - /** - * A built-in type, which is usable as a dependency in a service implementation. - * This needs to be kept up-to-date with the {@see BUILT_INS} constant. - * @ignore - */ - type BuiltIn = - | typeof String - | typeof Number - | typeof Boolean - | typeof Symbol - | typeof Object - | typeof Array; - - /** - * Map a built-in to its native type. - * @ignore - * - * As built-in types can only be expressed using constructors, - * we need to cast them to their TypeScript-friendly type, as - * otherwise classes using these built-in types would be forced - * to accept, for example, a String *instance* (`new String`) in the place of a String dependency. - * - * The [TypeScript *Do's And Don'ts* docs][dos-and-donts] suggests the following transformations: - * - * | Dependency | TS Type | - * |----------------|-----------| - * | **String** | `string` | - * | **Number** | `number` | - * | **Boolean** | `boolean` | - * | **Symbol** | `symbol` | - * | **Object** | `object` | - * - * We extend this by also casting Array dependencies to `unknown[]`. - * - * [dos-and-donts]: https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html - */ - export type MapBuiltInToNativeType = - T extends typeof String ? string : - T extends typeof Number ? number : - T extends typeof Boolean ? boolean : - T extends typeof Symbol ? symbol : - T extends typeof Object ? object : - T extends typeof Array ? unknown[] : - T; - - /** - * Map an `AnyServiceDependency` type to its true value. - * For example, in the case of a `Token`, `string` is returned. - * @ignore - */ - // prettier-ignore - export type UnpackServiceDependency = +import { LazyReference } from './lazy-reference.type'; +import { ServiceIdentifier } from './service-identifier.type'; + +/** + * @fileoverview @ignore + * The premise of this file is to allow for the conversion of service dependency lists into types which are appropriate + * for consuming as part of the service's constructor. + * + * The main focus of the implementation is below, in the `UnpackServiceDependency` type. This type transforms + * dependencies into their actual values. + * + * For a constructor passed into `ServiceSubject`, an "expected type" is returned. + * + * Consider the following example: + * ```ts + * @Service([AnotherService]) class MyService {} + * ``` + * + * In this case, as the `Service` overloads statically determines the class' dependencies, + * it's able to create a type which it *expects* the constructor to be. + * Considering the above example, the overload would expect the constructor it's going to + * operate on to be of type `new (arg1: AnotherService) => unknown`. + * + * If the type doesn't match (so say the constructor implementation expects another + * type in that dependency's place), the code won't compile without using `any`. + * + * Therefore, the following (thankfully) wouldn't compile: + * ```ts + * @Service([AnotherService]) + * class MyService { + * constructor (private value: number) { } + * } + * ``` + * + * If you look below, we also guard against built-in types surfacing into type design. For instance, if a class uses + * Number as a dependency, the accompanying argument in the constructor would be `number` instead of the discouraged + * `Number` type. + * + * Consider the following example: + * ```ts + * @Service({ factory: () => new Car(2) }, [Number]) + * class Car { + * constructor (private modelNumber: number) { } + * } + * ``` + * + * Instead of using `Number` in the constructor, we're able to use `number` in its place. This is because, for each + * dependency, we check if the type matches any of the built-in constructors, such as Number, String and Object. If they + * do, they're cast to their underlying native types. In the case of Number such as in the above example, that type is + * cast to `number`. The type compiler below is specially designed to accommodate this use-case. + * + * --- + * + * One note is that in the implementation, the expected type's return value is `unknown`. This is fine, as the return + * types of decorators doesn't actually matter to TypeScript. This makes the implementation a little bit simpler. + * + * The advantage of this overall approach is that it mostly keeps the type-checking decoupled from the Service decorator + * implementation. The actual type-checking is performed in this file. Furthermore, we get type-checking basically for + * free; this doesn't impact the size of the final bundle at all. Sweet! + * + * One notable drawback of this implementation is that, without modifications to TypeScript, we've absolutely no way to + * force `Optional`-marked parameters to possibly expect a null type. In the case of our resolution constraint bitmask, + * it's cast to a number internally. We can't check individual flags against it. + */ + +/** + * A built-in type, which is usable as a dependency in a service implementation. + * This needs to be kept up-to-date with the {@see BUILT_INS} constant. + * @ignore + */ +type BuiltIn = typeof String | typeof Number | typeof Boolean | typeof Symbol | typeof Object | typeof Array; + +/** + * Map a built-in to its native type. + * @ignore + * + * As built-in types can only be expressed using constructors, + * we need to cast them to their TypeScript-friendly type, as + * otherwise classes using these built-in types would be forced + * to accept, for example, a String *instance* (`new String`) in the place of a String dependency. + * + * The [TypeScript *Do's And Don'ts* docs][dos-and-donts] suggests the following transformations: + * + * | Dependency | TS Type | + * |----------------|-----------| + * | **String** | `string` | + * | **Number** | `number` | + * | **Boolean** | `boolean` | + * | **Symbol** | `symbol` | + * | **Object** | `object` | + * + * We extend this by also casting Array dependencies to `unknown[]`. + * + * [dos-and-donts]: https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html + */ +export type MapBuiltInToNativeType = T extends typeof String + ? string + : T extends typeof Number + ? number + : T extends typeof Boolean + ? boolean + : T extends typeof Symbol + ? symbol + : T extends typeof Object + ? object + : T extends typeof Array + ? unknown[] + : T; + +/** + * Map an `AnyServiceDependency` type to its true value. + * For example, in the case of a `Token`, `string` is returned. + * @ignore + */ +// prettier-ignore +export type UnpackServiceDependency = /** Map [type, Constraints] pairs to the base type for easier unwrapping. */ T extends DependencyPairWithConfiguration ? UnpackServiceDependency : @@ -150,39 +149,38 @@ import { Token } from '../token.class'; */ T extends LazyReference ? MapBuiltInToNativeType> : never; - + +/** + * Create an appropriate type for a decorator's subject constructor, + * where T is equal to the type of instance expected, and TDeps is + * equal to its dependencies. + */ +export type ServiceWithDependencies = /** - * Create an appropriate type for a decorator's subject constructor, - * where T is equal to the type of instance expected, and TDeps is - * equal to its dependencies. - */ - export type ServiceWithDependencies = - /** - * We can't use the TypedConstructable type here as otherwise, - * TypeScript complains that the type instantiation is "excessively deep". - * - * While we're not sure how / why this fixes the issue, it does. - * - * Full TS error text for reference: - * Type instantiation is excessively deep and possibly infinite.ts(2589) - */ - new (...args: UnpackDependencies) => T; - - /** - * Unpack a list of dependencies for a service, mapping each one - * to an appropriate type for the service's constructor. - * - * The returned type is compatible with the array type. - * - * @remarks - * The implementation of this type creates a dependency on a TypeScript version over - * or equal to vxx.xx.xx. This is due to prior versions not allowing mapped arrays - * to be presented as rest parameters for function / constructors. - * [The original issue, present since 2019](https://github.com/microsoft/TypeScript/issues/29919), - * was fixed in a [pull request](https://github.com/microsoft/TypeScript/pull/49947) in 2023. - * Thanks to Andarist for the great work! + * We can't use the TypedConstructable type here as otherwise, + * TypeScript complains that the type instantiation is "excessively deep". + * + * While we're not sure how / why this fixes the issue, it does. + * + * Full TS error text for reference: + * Type instantiation is excessively deep and possibly infinite.ts(2589) */ - export type UnpackDependencies = { - [TIndex in keyof TDeps]: UnpackServiceDependency; - }; - \ No newline at end of file + new (...args: UnpackDependencies) => T; + +/** + * Unpack a list of dependencies for a service, mapping each one + * to an appropriate type for the service's constructor. + * + * The returned type is compatible with the array type. + * + * @remarks + * The implementation of this type creates a dependency on a TypeScript version over + * or equal to vxx.xx.xx. This is due to prior versions not allowing mapped arrays + * to be presented as rest parameters for function / constructors. + * [The original issue, present since 2019](https://github.com/microsoft/TypeScript/issues/29919), + * was fixed in a [pull request](https://github.com/microsoft/TypeScript/pull/49947) in 2023. + * Thanks to Andarist for the great work! + */ +export type UnpackDependencies = { + [TIndex in keyof TDeps]: UnpackServiceDependency; +}; diff --git a/test/decorators/Service.spec.ts b/test/decorators/Service.spec.ts index dea66569..f1c6bfd7 100644 --- a/test/decorators/Service.spec.ts +++ b/test/decorators/Service.spec.ts @@ -7,7 +7,7 @@ import { TypedService } from '../../src/contrib/typed-service.decorator'; */ describe.each([ { name: 'TypedService', decorator: TypedService }, - { name: 'Service', decorator: Service } + { name: 'Service', decorator: Service }, ])('$name decorator', ({ decorator: baseDecorator }) => { /** Casting here avoids a compilation error. */ const decorator = baseDecorator as typeof Service; diff --git a/test/features/contrib/typed-service.spec.ts b/test/features/contrib/typed-service.spec.ts index de54a966..add7507e 100644 --- a/test/features/contrib/typed-service.spec.ts +++ b/test/features/contrib/typed-service.spec.ts @@ -1,5 +1,3 @@ import { TypedService } from '../../../src/contrib/typed-service.decorator'; -describe('TypedService', () => { - -}); +describe('TypedService', () => {});