From d13df64122aa876a25c153277935c400db8e3c22 Mon Sep 17 00:00:00 2001 From: lorenzofox3 Date: Sat, 26 Oct 2024 18:27:08 +0200 Subject: [PATCH 1/2] cleaning up code --- src/index.d.ts | 54 ++++++++++++++++++++-------------- src/test.ts | 78 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 87 insertions(+), 45 deletions(-) diff --git a/src/index.d.ts b/src/index.d.ts index 57cf79d..87e6992 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -20,7 +20,7 @@ type Dependencies = Defined>; export type Injectable = ReturnType>; type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I + k: infer I, ) => void ? I : never; @@ -40,21 +40,23 @@ export type InjectableMap = { [key in keyof Registry]: Injectable; }; -type MaybeMet = - keyof FlatDependencyTree & keyof InjectableMap; - /** - * Dependencies already met by the injectables themselves (union of keys) + * Dependencies already met by the injectables themselves */ -export type FulfilledDependencies = { - [Dep in MaybeMet]: InjectableMap[Dep] extends FlatDependencyTree[Dep] +export type FulfilledDependencies< + Registry extends ObjectLike, + Dependencies = FlatDependencyTree, + Injectables = InjectableMap, +> = { + [Dep in keyof Dependencies & + keyof Injectables as Injectables[Dep] extends Dependencies[Dep] ? Dep - : never; -}[MaybeMet]; + : never]: Dependencies[Dep]; +}; export type ExternalDeps = Omit< FlatDependencyTree, - FulfilledDependencies + keyof FulfilledDependencies > & Partial>; @@ -70,23 +72,33 @@ type ProviderFnArgs = { export type ProviderFn< Registry extends ObjectLike, - PublicAPI extends Array = [] -> = Partial> extends ExternalDeps - ? (externalDeps?: ProviderFnArgs) => ModuleAPI - : (externalDeps: ProviderFnArgs) => ModuleAPI; + PublicAPI extends Array = [], +> = + FulfilledDependencies extends FlatDependencyTree + ? ( + externalDeps?: ProviderFnArgs, + ) => ModuleAPI + : ( + externalDeps: ProviderFnArgs, + ) => ModuleAPI; /** * If the injectable is a function, we have to wrap it in a function to avoid treating it as a factory */ type WrapFunctionInjectable = [T] extends [(...args: any[]) => any] - ? (deps?: any) => T + ? (deps?: any) => T : ((deps?: any) => T) | T; - + /** * Checks if each injectable match the required dependencies of the entire registry */ -type ValidateRegistry> = { - [key in keyof Registry]: key extends keyof Deps ? WrapFunctionInjectable : Registry[key]; +type ValidateRegistry< + Registry extends ObjectLike, + Deps = FlatDependencyTree, +> = { + [key in keyof Registry]: key extends keyof Deps + ? WrapFunctionInjectable + : Registry[key]; }; declare function valueFn(value: T): () => T; @@ -94,17 +106,17 @@ declare function valueFn(value: T): () => T; declare const provideSymbol: unique symbol; declare function singleton any>( - factory: Factory + factory: Factory, ): (...args: Parameters) => ReturnType; declare function createProvider< Registry extends ObjectLike, - PublicAPI extends Array = [] + PublicAPI extends Array = [], >(args: { injectables: ValidateRegistry; api?: PublicAPI; }): ProviderFn; declare function fromClass any>( - Klass: T + Klass: T, ): (deps: Defined[0]>) => InstanceType; diff --git a/src/test.ts b/src/test.ts index e24c11b..b635f1c 100644 --- a/src/test.ts +++ b/src/test.ts @@ -112,31 +112,62 @@ fulfilledDependencies: { foo: (arg: { x: number; blah: string; woot: { prop: number } }) => any; x: ({ otherThing }: { otherThing: string; y: string }) => number; woot: () => { prop: number }; - }> = 'x'; - fulfilled = 'woot'; + }> = { + woot: { prop: 42 }, + x: 42, + }; - // @ts-expect-error - // blah is not met - fulfilled = 'blah'; + fulfilled = { + woot: { prop: 42 }, + x: 42, + // @ts-expect-error + // blah is not met + blah: 'hello', + }; - // @ts-expect-error - // otherThing is not met - fulfilled = 'otherThing'; + fulfilled = { + woot: { prop: 42 }, + x: 42, + // @ts-expect-error + // otherThing is not met + otherThing: 'hello', + }; + + fulfilled = { + // @ts-expect-error + // prop should be a number + woot: { prop: 'hello' }, + x: 42, + }; let incompatibleInterfaces: FulfilledDependencies<{ x: (deps: { y: number; woot: { prop: { nested: number } } }) => any; y: () => string; woot: (deps: { met: string }) => { prop: { nested: string } }; met: () => string; - }> = 'met'; + }> = { + met: 'hello', + }; - // @ts-expect-error - // y should return a number - incompatibleInterfaces = 'y'; + incompatibleInterfaces = { + met: 'hello', + // @ts-expect-error + // y should return a number + y: 'hello', - // @ts-expect-error - // nested type should be number - incompatibleInterfaces = 'woot'; + woot: { + prop: { nested: 'woot' }, + }, + }; + + incompatibleInterfaces = { + met: 'hello', + // @ts-expect-error + // nested should be a number + woot: { + prop: { nested: 'woot' }, + }, + }; } lateBoundDependencies: { @@ -215,8 +246,8 @@ createProvider: { bar: ({ b }: { b: string }) => b, baz: ({ c }: { c: () => boolean }) => c(), // @ts-expect-error a is not a number - a: "42", - // @ts-expect-error b is not a string + a: '42', + // @ts-expect-error b is not a string b: () => 42 as number, // @ts-expect-error c has to be wrapped in a function c: () => true, @@ -271,7 +302,7 @@ createProvider: { provideDeepMissing({ typedDep: 'toto', nonTypedDep: 42 }); // @ts-expect-error typedDep & nonTypedDep is missing here provideDeepMissing(); - // @ts-expect-error typedDep & nonTypedDep is missing here + // @ts-expect-error typedDep & nonTypedDep is missing here provideDeepMissing({}); const provideWrongType = createProvider({ @@ -281,12 +312,12 @@ createProvider: { api: ['foo'], }); // @ts-expect-error wrong dependency type - provideWrongType({ val: "42" }) + provideWrongType({ val: '42' }); } fromClass: { class Foo { - constructor({ b }: { b: string }) { } + constructor({ b }: { b: string }) {} } let factory = fromClass(Foo); factory({ b: 'woot' }); @@ -309,11 +340,10 @@ issue4: { }; const provideMissingWithIntermediate = createProvider({ injectables: injectables, - api: ['a'] + api: ['a'], }); - provideMissingWithIntermediate({ - value: ({ intermediate }: { intermediate: string }) => Number(intermediate) - }) + value: ({ intermediate }: { intermediate: string }) => Number(intermediate), + }); } From 7f40ab8b7590b05e7324e7a204949db02e1f1421 Mon Sep 17 00:00:00 2001 From: lorenzofox3 Date: Sat, 26 Oct 2024 18:30:04 +0200 Subject: [PATCH 2/2] streamline formating --- src/provider.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/provider.js b/src/provider.js index 4191ed3..33f3451 100644 --- a/src/provider.js +++ b/src/provider.js @@ -3,7 +3,7 @@ const mapValues = (mapFn) => (source) => [ ...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source), - ].map((key) => [key, mapFn(source[key], key)]) + ].map((key) => [key, mapFn(source[key], key)]), ); export const valueFn = (val) => () => val; @@ -31,7 +31,7 @@ export const createProvider = ({ injectables, api = [] }) => { provide({ ...externalDeps, ...subArgs, - }) + }), ), ...externalDeps, }, @@ -40,13 +40,13 @@ export const createProvider = ({ injectables, api = [] }) => { if (!(prop in target)) { throw new Error( `could not resolve injectable with injection token "${String( - prop - )}"` + prop, + )}"`, ); } return Reflect.get(target, prop, receiver); }, - } + }, ); const mapWithPropertyDescriptor = mapValues((factory, key) => { @@ -61,6 +61,7 @@ export const createProvider = ({ injectables, api = [] }) => { }); const properties = mapWithPropertyDescriptor(_injectables); + return Object.defineProperties(_injectables, properties); }; };