-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Combination of 20 provideValue()/provideFactory() calls make vscode unusable #50
Comments
Would a non-chainable API help you here? I mean, don't you have dependencies that depend on each other? I.e. would you expect something like this to work? interface Foo {
foo: string;
}
class Bar {
static readonly inject = ['foo'] as const;
constructor(foo: Foo){ }
}
rootInjector.provide({
foo: { value: { foo: 'hello'} },
bar: { clazz: Bar }
}) Because something like that might be impossible to get statically type-checked. |
Thanks for the response @nicojs. I see what you are saying. I actually took a stab at this today except using a proxy so I could do stuff like container.dep instead of container.resolve("dep"). The most type safe I could get it is was where the inject tokens had to be part of the keys passed in, and that the factory func args had to at least be one of the resulting types of the factory functions (or static values). It would be great to somehow link the args names to a type. When I have more time I may dig deeper into how you were doing your typing to understand if this is possible but I've kind of got something that unblocks me and has "better than nothing" typing lol. Here is what I ended up with if you are interested: type AsContainerWithInstanceTypes<TIn extends Record<string, ((...args: any) => any) | { value: any }>> = {
[prop in keyof TIn as prop]: TIn[prop] extends { value: any }
? TIn[prop]["value"]
: TIn[prop] extends (...args: any) => any
? ReturnType<TIn[prop]>
: unknown;
};
export function createDiContainer<
T extends Record<
string,
| (((...args: AsContainerWithInstanceTypes<T>[keyof AsContainerWithInstanceTypes<T>][]) => any) & {
inject?: readonly (keyof T)[];
})
| { value: any }
>,
>(
dependencies: T,
options?: { logger?: Logger; onResolve?: (prop: string, val: any) => void },
): AsContainerWithInstanceTypes<T> {
const _containerCache: Map<string, any> = new Map();
const _onResolve = (prop: string, val: any) => {
if (options?.onResolve) {
options?.onResolve(prop, val);
}
_containerCache.set(prop, val);
options?.logger?.debug(`returning ${prop} for first time`);
return val;
};
const handler = {
get: function (target: any, prop: string, receiver: any) {
options?.logger?.debug(`resolving ${prop}`);
if (_containerCache.has(prop)) {
options?.logger?.debug(`returning ${prop} from cache`);
return _containerCache.get(prop);
}
const targetDep = dependencies[prop];
if (!targetDep) {
throw new Error(`dependency "${prop}" not registered with container!`);
}
// TODO: implement classes. -mc
if (!_.isFunction(targetDep)) {
const resolved = targetDep.value;
return _onResolve(prop, resolved);
}
const paramNames = (targetDep as any).inject || [];
options?.logger?.debug(
`${prop} has ${paramNames.length === 0 ? "no dependencies" : `dependencies: ${paramNames.join(", ")}`}`,
);
const params = paramNames.map((dep: string) => this.get(target, dep, receiver));
const resolved = targetDep(...params);
return _onResolve(prop, resolved);
},
};
return new Proxy(dependencies, handler);
} |
It was a disaster, I spent a lot of time understanding what was happening with VSCode and eventually, I found this issue. I really like this library but my project got stuck when the number of provideClass came to 20. I really don't know what to do. Are there any ways to resolve this issue? Or I should refactor the whole project using another library?((( |
The same for me. |
Chiming in to say that I got the same error as @fobdy on my 21st |
The So I patched to diff --git a/node_modules/typed-inject/dist/src/api/Injector.d.ts b/node_modules/typed-inject/dist/src/api/Injector.d.ts
index 9375bb6..49fed38 100644
--- a/node_modules/typed-inject/dist/src/api/Injector.d.ts
+++ b/node_modules/typed-inject/dist/src/api/Injector.d.ts
@@ -2,13 +2,14 @@ import { InjectableClass, InjectableFunction } from './Injectable';
import { InjectionToken } from './InjectionToken';
import { Scope } from './Scope';
import { TChildContext } from './TChildContext';
export interface Injector<TContext = {}> {
injectClass<R, Tokens extends readonly InjectionToken<TContext>[]>(Class: InjectableClass<TContext, R, Tokens>): R;
injectFunction<R, Tokens extends readonly InjectionToken<TContext>[]>(Class: InjectableFunction<TContext, R, Tokens>): R;
resolve<Token extends keyof TContext>(token: Token): TContext[Token];
- provideValue<Token extends string, R>(token: Token, value: R): Injector<TChildContext<TContext, R, Token>>;
- provideClass<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(token: Token, Class: InjectableClass<TContext, R, Tokens>, scope?: Scope): Injector<TChildContext<TContext, R, Token>>;
- provideFactory<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(token: Token, factory: InjectableFunction<TContext, R, Tokens>, scope?: Scope): Injector<TChildContext<TContext, R, Token>>;
+ provideValue<Token extends string, R>(token: Token extends keyof TContext ? never : Token, value: R): Injector<{ [K in keyof TContext]: TContext[K]} & { [T in Token]: R }>;
+ provideClass<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(token: Token extends keyof TContext ? never : Token, Class: InjectableClass<TContext, R, Tokens>, scope?: Scope): Injector<{ [K in keyof TContext]: TContext[K]} & { [T in Token]: R }>;
+ provideFactory<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(token: Token extends keyof TContext ? never : Token, factory: InjectableFunction<TContext, R, Tokens>, scope?: Scope): Injector<{ [K in keyof TContext]: TContext[K]} & { [T in Token]: R }>;
dispose(): Promise<void>;
}
//# sourceMappingURL=Injector.d.ts.map
\ No newline at end of file // argument (to prevent from providing value with same token)
token: Token
// ↓
token: Token extends keyof TContext ? never : Token
// return type
Injector<TChildContext<TContext, R, Token>>
// ↓
Injector<{ [K in keyof TContext]: TContext[K]} & { [T in Token]: R }>; https://github.com/nasuboots/typed-inject-types Then I can chain the methods up to 49 without error. Also, it makes very faster to resolve type checking. Over 49 method chains cause error: "Type instantiation is excessively deep and possibly infinite". |
Firstly, I just want to say what a great concept this API is, very clean and easy to use, and that I really would like to use in my code base compared to the other typescript DI frameworks out there.
But as I have been adding more factories vscode has become unusable, when I make a change it will take 10 seconds for the intellisense to catch up (and my fans go on lol). I started out with only 5 .provideFactory()s and everything was fine, but I ported more of my code over and things started to get slow. Now it's clear when I comment out the new .provideFactory()s vscode will start behaving again. Right now I have a total of 4 provideValue() and 15 provideFactory() calls. If I had to guess there is some kind of extra work in tsc because the builder pattern might be nesting copies of same type over an over again in the generics.
There was another issue a few years ago that asked for a non-chained version of the API, I'm not sure if something like that would help in this situation #22
Typescript version I am using is is 4.5.5, vscode typescript default of 4.6.3 does not seem to change anything.
The text was updated successfully, but these errors were encountered: