1 | // Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
2 | // Node module: @loopback/repository
|
3 | // This file is licensed under the MIT License.
|
4 | // License text available at https://opensource.org/licenses/MIT
|
5 |
|
6 | import {Class} from './common-types';
|
7 |
|
8 | /**
|
9 | * A type resolver is a function that returns a class representing the type,
|
10 | * typically a Model or Entity (e.g. Product).
|
11 | *
|
12 | * We use type resolvers to break require() loops when defining relations.
|
13 | * The target model (class) is provided via a provider, thus deferring
|
14 | * the actual reference to the class itself until later, when both sides
|
15 | * of the relation are created as JavaScript classes.
|
16 | *
|
17 | * @typeParam Type - The type we are resolving, for example `Entity` or `Product`.
|
18 | * This parameter is required.
|
19 | *
|
20 | * @typeParam StaticMembers - The static properties available on the
|
21 | * type class. For example, all models have static `modelName` property.
|
22 | * When `StaticMembers` are not provided, we default to static properties of
|
23 | * a `Function` - `name`, `length`, `apply`, `call`, etc.
|
24 | * Please note the value returned by the resolver is described as having
|
25 | * arbitrary additional static properties (see how Class is defined).
|
26 | */
|
27 | export type TypeResolver<
|
28 | Type extends Object,
|
29 | StaticMembers = Function,
|
30 | > = () => Class<Type> & StaticMembers;
|
31 |
|
32 | /**
|
33 | * A function that checks whether a function is a TypeResolver or not.
|
34 | * @param fn - The value to check.
|
35 | */
|
36 | export function isTypeResolver<T extends object>(
|
37 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
38 | fn: any,
|
39 | ): fn is TypeResolver<T> {
|
40 | // 1. A type provider must be a function
|
41 | if (typeof fn !== 'function') return false;
|
42 |
|
43 | // 2. A class constructor is not a type provider
|
44 | if (/^class/.test(fn.toString())) return false;
|
45 |
|
46 | // 3. Built-in types like Date & Array are not type providers
|
47 | if (isBuiltinType(fn)) return false;
|
48 |
|
49 | // TODO(bajtos): support model classes defined via ES5 constructor function
|
50 |
|
51 | return true;
|
52 | }
|
53 |
|
54 | /**
|
55 | * A boxed type for `null`
|
56 | */
|
57 | // eslint-disable-next-line @typescript-eslint/naming-convention
|
58 | export function Null() {
|
59 | return null;
|
60 | }
|
61 |
|
62 | /**
|
63 | * Check if the provided function is a built-in type provided by JavaScript
|
64 | * and/or Node.js. E.g. `Number`, `Array`, `Buffer`, etc.
|
65 | */
|
66 | export function isBuiltinType(fn: Function): boolean {
|
67 | return (
|
68 | // scalars
|
69 | fn === Number ||
|
70 | fn === Boolean ||
|
71 | fn === String ||
|
72 | // objects
|
73 | fn === Object ||
|
74 | fn === Array ||
|
75 | fn === Date ||
|
76 | fn === RegExp ||
|
77 | fn === Buffer ||
|
78 | fn === Null ||
|
79 | // function as a type
|
80 | fn === Function
|
81 | );
|
82 | }
|
83 |
|
84 | export type NonFunction<T> = T extends Function ? never : T;
|
85 |
|
86 | /**
|
87 | * Resolve a type value that may have been provided via TypeResolver.
|
88 | * @param fn - A type class or a type provider.
|
89 | * @returns The resolved type.
|
90 | */
|
91 | export function resolveType<T extends object>(
|
92 | fn: TypeResolver<T, {}> | Class<T> | Function,
|
93 | ): Class<T>;
|
94 |
|
95 | // An overload to handle the case when `fn` is not a class nor a resolver.
|
96 | export function resolveType<T>(fn: NonFunction<T>): T;
|
97 |
|
98 | export function resolveType<T extends object>(
|
99 | fn: TypeResolver<T> | Class<T> | T,
|
100 | ) {
|
101 | return isTypeResolver(fn) ? fn() : fn;
|
102 | }
|