##// END OF EJS Templates
improved typings
cin -
r7:58282d42a47b default
parent child
Show More
@@ -1,143 +1,154
1 import { key } from "./traits";
1 import { key } from "./traits";
2
2
3 export interface IDestroyable {
3 export interface IDestroyable {
4 destroy(): void;
4 destroy(): void;
5 }
5 }
6
6
7 /**
7 /**
8 * @template S Карта доступных зависимостей
8 * @template S Карта доступных зависимостей
9 */
9 */
10 export interface Resolver<S extends object> {
10 export interface Resolver<S extends object> {
11 /**
11 /**
12 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
12 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
13 * отложенную активацию и значение по-умолчанию для сервисов
13 * отложенную активацию и значение по-умолчанию для сервисов
14 * @template K Ключ сервиса из {@link S}
14 * @template K Ключ сервиса из {@link S}
15 * @template O Тип параметра {@link opts} используется для выведения типа
15 * @template O Тип параметра {@link opts} используется для выведения типа
16 * возвращаемого значения.
16 * возвращаемого значения.
17 * @param name Ключ сервиса, который будет разрешен.
17 * @param name Ключ сервиса, который будет разрешен.
18 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
18 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
19 * будет возвращен фабричный метод для получения зависимости. Если не указан,
19 * будет возвращен фабричный метод для получения зависимости. Если не указан,
20 * то считается `false`.
20 * то считается `false`.
21 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
21 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
22 * сервис не зарегистрирован
22 * сервис не зарегистрирован
23 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
23 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
24 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
24 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
25 */
25 */
26 <K extends keyof S, O extends { lazy: true; default?: unknown }>(name: K, opts?: O): () => (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
26 <K extends keyof S, O extends { lazy: true; default?: unknown }>(name: K, opts?: O): () => (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
27 <K extends keyof S, O extends { lazy?: false; default?: unknown }>(name: K, opts?: O): (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
27 <K extends keyof S, O extends { lazy?: false; default?: unknown }>(name: K, opts?: O): (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
28 }
28 }
29
29
30 export type DepsMap<K extends key, SK extends key> = { [k in K]: SK | Ref<SK, boolean, unknown> };
30 export type DepsMap<S extends object> = {
31 [k in key]: Refs<S>
32 };
33
34 export type Refs<S extends object> = {
35 [k in keyof S]: Ref<k, boolean, S[k]>;
36 }[keyof S] | keyof S;
37
38 export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L, default?: D | null };
31
39
32 export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L } | { name: K, lazy?: L, default: D };
40 export type Lazy<T, L extends boolean> = L extends true ? () => T : T;
41
42 export type InferDefault<T> = T extends { default: infer D } ? D : never;
33
43
34 export type Resolved<S, K extends keyof S, L, D> =
44 export type Resolve<S extends object, R> =
35 L extends true ? () => NonNullable<S[K]> | (unknown extends D ? never : D) : NonNullable<S[K]> | (unknown extends D ? never : D);
45 R extends keyof S ? NonNullable<S[R]> :
46 R extends Ref<infer K, infer L, unknown> ?
47 K extends keyof S ? Lazy<NonNullable<S[K]> | InferDefault<R>, L> :
48 never:
49 never;
36
50
37 export interface IDescriptorBuilder<S extends object, T, R extends object, O extends keyof S> {
51 export interface IDescriptorBuilder<S extends object, T, R extends object, O extends keyof S> {
38
52
39 /**
53 /**
40 *
54 *
41 * @param f
55 * @param f
42 */
56 */
43 factory(f: (refs: R) => T): void;
57 factory(f: (refs: R) => T): void;
44
58
45 wants<X extends DepsMap<Exclude<key, keyof R>, keyof S>>(refs: X):
59 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
46 IDescriptorBuilder<S, T, R & {
60 IDescriptorBuilder<S, T, R & {
47 [k in keyof X]:
61 [k in keyof X]: Resolve<S, X[k]>;
48 X[k] extends keyof S ? NonNullable<S[X[k]]> :
49 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
50 never
51 }, O>
62 }, O>
52
63
53 override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
64 override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
54 override<K extends O>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
65 override<K extends O>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
55
66
56 lifetime(lifetime: "singleton", typeId: string | number | object): this;
67 lifetime(lifetime: "singleton", typeId: string | number | object): this;
57 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
68 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
58
69
59 cleanup(cb: (item: T) => void): this;
70 cleanup(cb: (item: T) => void): this;
60
71
61 value(v: T): void;
72 value(v: T): void;
62 }
73 }
63
74
64 export type RegistrationBuilder<S extends object, T> = (d: IDescriptorBuilder<S, T, object, ConfigurableKeys<S>>) => void;
75 export type RegistrationBuilder<S extends object, T> = (d: IDescriptorBuilder<S, T, object, ConfigurableKeys<S>>) => void;
65
76
66 export type RegistrationBuildersMap<S extends Configurable<S>, K extends keyof S = keyof S> = {
77 export type RegistrationBuildersMap<S extends Configurable<S>, K extends keyof S = keyof S> = {
67 [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<S[k]>>
78 [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<S[k]>>
68 };
79 };
69
80
70 export interface Descriptor<S extends object, T> {
81 export interface Descriptor<S extends object, T> {
71 activate(context: IActivationContext<S>): T;
82 activate(context: IActivationContext<S>): T;
72 }
83 }
73
84
74 export interface IActivationContext<S extends object> extends ServiceLocator<S> {
85 export interface IActivationContext<S extends object> extends ServiceLocator<S> {
75 createLifetime<T>(): ILifetime<T>;
86 createLifetime<T>(): ILifetime<T>;
76
87
77 createContainerLifetime<T>(): ILifetime<T>;
88 createContainerLifetime<T>(): ILifetime<T>;
78 }
89 }
79
90
80 export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
91 export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
81 [k in K]-?: Descriptor<S, S[k]>;
92 [k in K]-?: Descriptor<S, S[k]>;
82 };
93 };
83
94
84 export interface ContainerProvided<S extends Configurable<S>> {
95 export interface ContainerProvided<S extends Configurable<S>> {
85 container: ServiceLocator<ContainerServices<S>>;
96 container: ServiceLocator<ContainerServices<S>>;
86
97
87 childContainer: IContainerBuilder<S>;
98 childContainer: IContainerBuilder<S>;
88 }
99 }
89
100
90 export type Configurable<S> = { [k in keyof S & (keyof ContainerProvided<never>)]: never; };
101 export type Configurable<S> = { [k in keyof S & (keyof ContainerProvided<never>)]: never; };
91
102
92 export type ProvidedKeys = keyof ContainerProvided<never>;
103 export type ProvidedKeys = keyof ContainerProvided<never>;
93
104
94 export type ContainerServices<S extends Configurable<S>> = S & ContainerProvided<S>;
105 export type ContainerServices<S extends Configurable<S>> = S & ContainerProvided<S>;
95
106
96 export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
107 export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
97
108
98 export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
109 export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
99
110
100 export type ContainerKeys<S extends Configurable<S>> = keyof S | keyof ContainerProvided<never>;
111 export type ContainerKeys<S extends Configurable<S>> = keyof S | keyof ContainerProvided<never>;
101
112
102 export interface ServiceLocator<S extends object> {
113 export interface ServiceLocator<S extends object> {
103 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
114 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
104 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
115 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
105 }
116 }
106
117
107 export interface LifetimeContainer {
118 export interface LifetimeContainer {
108 createLifetime<T>(): ILifetime<T>;
119 createLifetime<T>(): ILifetime<T>;
109 }
120 }
110
121
111 export interface ServiceContainer<S extends Configurable<S>> extends
122 export interface ServiceContainer<S extends Configurable<S>> extends
112 ServiceLocator<ContainerServices<S>>,
123 ServiceLocator<ContainerServices<S>>,
113 IDestroyable {
124 IDestroyable {
114
125
115 createChildContainer(): IContainerBuilder<S>;
126 createChildContainer(): IContainerBuilder<S>;
116 }
127 }
117
128
118 export interface IContainerBuilder<S extends Configurable<S>> {
129 export interface IContainerBuilder<S extends Configurable<S>> {
119 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object, keyof S>;
130 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object, keyof S>;
120
131
121 build(): ServiceContainer<S>;
132 build(): ServiceContainer<S>;
122 }
133 }
123
134
124
135
125 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
136 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
126
137
127 /**
138 /**
128 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
139 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
129 * свой собственный объект `ILifetime`, который создается при первой активации
140 * свой собственный объект `ILifetime`, который создается при первой активации
130 */
141 */
131 export interface ILifetime<T> {
142 export interface ILifetime<T> {
132 /** Проверяет, что уже создан экземпляр объекта */
143 /** Проверяет, что уже создан экземпляр объекта */
133 has(): boolean;
144 has(): boolean;
134
145
135 get(): T;
146 get(): T;
136
147
137 initialize(context: IActivationContext<object>): void;
148 initialize(context: IActivationContext<object>): void;
138
149
139 store(item: T, cleanup?: (item: T) => void): void;
150 store(item: T, cleanup?: (item: T) => void): void;
140 }
151 }
141
152
142 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
153 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
143
154
@@ -1,70 +1,87
1 /* eslint max-classes-per-file: ["error", 20] */
1 /* eslint max-classes-per-file: ["error", 20] */
2 import { describe, it } from "mocha";
2 import { describe, it } from "mocha";
3 import { Container } from "../Container";
3 import { Container } from "../Container";
4 import { ContainerServices } from "../interfaces";
4 import { ContainerServices, DepsMap, Refs, Resolver } from "../interfaces";
5 import { fluent } from "../traits";
5 import { fluent } from "../traits";
6
6
7 class Foo {
7 class Foo {
8 foo = "foo";
8 foo = "foo";
9 }
9 }
10
10
11 class Bar {
11 class Bar {
12 bar = "bar";
12 bar = "bar";
13
13
14 constructor(foo?: () => Foo) { }
14 constructor(foo?: () => Foo) { }
15 }
15 }
16
16
17 interface Services {
17 interface Services {
18 foo: Foo;
18 foo: Foo;
19
19
20 bar?: Bar;
20 bar?: Bar;
21
21
22 baz: Foo;
22 baz: Foo;
23 }
23 }
24
24
25 interface ServicesB {
25 interface ServicesB {
26 // will give errors
26 // will give errors
27 // baz: Bar;
27 // baz: Bar;
28
28
29 baz: Foo;
29 baz: Foo;
30
30
31 zoo?: Foo;
31 zoo?: Foo;
32 }
32 }
33
33
34 declare const resolver: Resolver<Services>;
35
36 const foo = resolver("foo", {lazy: true});
37
38 const mmap = <X extends DepsMap<Services, string>>(m: X) => {};
39
40 const refs: Refs<Services> = {name: "bar", default: undefined};
41
42 mmap({
43 fooz: {name: "foo", lazy: false, default: undefined },
44 ooz: "bar"
45 });
46
34 interface SharedServices {
47 interface SharedServices {
35 foo: Foo;
48 foo: Foo;
36
49
37 bar?: Bar;
50 bar?: Bar;
38
51
39 baz: Bar;
52 baz: Bar;
40 }
53 }
41
54
42 const config = fluent()
55 const config = fluent()
43 .declare<Services>()
56 .declare<Services>()
44 .declare<ServicesB>()
57 .declare<ServicesB>()
45 .register({
58 .register({
46 bar: it => it
59 bar: it => it
47 .lifetime("context") // тип активации, время жизни
60 .lifetime("context") // тип активации, время жизни
48 .wants({
61 .wants({
49 zoo: "zoo", // зависимость
62 zoo: "zoo", // зависимость
63 bar: "bar",
50
64
51 zoo$: { name: "zoo", lazy: true } // отложенная активация,
65 $zoo: { name: "foo", lazy: true, } // отложенная активация,
52 //фабричный метод
66 //фабричный метод
53 })
67 })
54 .factory(({ zoo$ }) => // фабрика получает объект с именованными зависимостями
68 .wants({
69 zoom: "bar"
70 })
71 .factory(({ $zoo, zoo }) => // фабрика получает объект с именованными зависимостями
55 // удобно для деструктурирования
72 // удобно для деструктурирования
56 new Bar(zoo$) // создается экземпляр сервиса
73 new Bar($zoo) // создается экземпляр сервиса
57 ),
74 ),
58 foo: it => it.factory(() => new Foo()),
75 foo: it => it.factory(() => new Foo()),
59 baz: it => it.value(new Foo())
76 baz: it => it.value(new Foo())
60 })
77 })
61 .done({});
78 .done({});
62
79
63 declare const container: Container<object>;
80 declare const container: Container<object>;
64 const c2 = config.configure(container);
81 const c2 = config.configure(container);
65
82
66 c2.resolve("foo");
83 c2.resolve("foo");
67
84
68 declare const m :ContainerServices<{foo: Foo}>["container"];
85 declare const m :ContainerServices<{foo: Foo}>["container"];
69
86
70 m.resolve("container").resolve("container"); No newline at end of file
87 m.resolve("container").resolve("container");
General Comments 0
You need to be logged in to leave comments. Login now