1 | // Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
|
2 | // Node module: @loopback/core
|
3 | // This file is licensed under the MIT License.
|
4 | // License text available at https://opensource.org/licenses/MIT
|
5 |
|
6 | import {Constructor} from '@loopback/context';
|
7 |
|
8 | /**
|
9 | * A replacement for `typeof Target` to be used in mixin class definitions.
|
10 | * This is a workaround for TypeScript limitation described in
|
11 | * - https://github.com/microsoft/TypeScript/issues/17293
|
12 | * - https://github.com/microsoft/TypeScript/issues/17744
|
13 | * - https://github.com/microsoft/TypeScript/issues/36060
|
14 | *
|
15 | * @example
|
16 | *
|
17 | * ```ts
|
18 | * export function MyMixin<T extends MixinTarget<Application>>(superClass: T) {
|
19 | * return class extends superClass {
|
20 | * // contribute new class members
|
21 | * }
|
22 | * };
|
23 | * ```
|
24 | *
|
25 | * TypeScript does not allow class mixins to access protected members from
|
26 | * the base class. You can use the following approach as a workaround:
|
27 | *
|
28 | * ```ts
|
29 | * // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
30 | * // @ts-ignore
|
31 | * (this as unknown as {YourBaseClass}).protectedMember
|
32 | * ```
|
33 | *
|
34 | * The directive `@ts-ignore` suppresses compiler error about accessing
|
35 | * a protected member from outside. Unfortunately, it also disables other
|
36 | * compile-time checks (e.g. to verify that a protected method was invoked
|
37 | * with correct arguments, and so on). This is the same behavior you
|
38 | * would get by using `Constructor<any>` instead of `MixinTarget<Application>`.
|
39 | * The major improvement is that TypeScript can still infer the return
|
40 | * type of the protected member, therefore `any` is NOT introduced to subsequent
|
41 | * code.
|
42 | *
|
43 | * TypeScript also does not allow mixin class to overwrite a method inherited
|
44 | * from a mapped type, see https://github.com/microsoft/TypeScript/issues/38496
|
45 | * As a workaround, use `@ts-ignore` to disable the error.
|
46 | *
|
47 | * ```ts
|
48 | * export function RepositoryMixin<T extends MixinTarget<Application>>(
|
49 | * superClass: T,
|
50 | * ) {
|
51 | * return class extends superClass {
|
52 | * // @ts-ignore
|
53 | * public component<C extends Component = Component>(
|
54 | * componentCtor: Constructor<C>,
|
55 | * nameOrOptions?: string | BindingFromClassOptions,
|
56 | * ) {
|
57 | * const binding = super.component(componentCtor, nameOrOptions);
|
58 | * // ...
|
59 | * return binding;
|
60 | * }
|
61 | * }
|
62 | * ```
|
63 | */
|
64 | export type MixinTarget<T extends object> = Constructor<{
|
65 | // Enumerate only public members to avoid the following compiler error:
|
66 | // Property '(name)' of exported class expression
|
67 | // may not be private or protected.ts(4094)
|
68 | [P in keyof T]: T[P];
|
69 | }>;
|