-
Notifications
You must be signed in to change notification settings - Fork 3
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
js module used as service token can lead to exception due to module cycles #18
Comments
Yeah that is unfortunate, there are many practical ways to resolve this, but they are not super elegant and I am not sure which if any we would want to recommend as the default, and I not sure that we want to preemptively encourage something more elaborate that avoids the problem. Essentially you just need to split apart the token and the definition/implementation (which has the reference that requires the other services). One way to do it: export default abstract class MyService {
}
// I think this is good enough to break the cycle? If not wrapping it in a closure would work
provide(Token, class ActualImplementation { ... }); Alternatively one/some of the services can forgo the declarators (which consumes the token values eagerly) and instead do it in the constructor Don't love that you have to do either of these but they should work. Would like for there to be something more elegant |
You could also do something like: export default abstract class MyService {
@service(() => Token) token: Token;
} And have the closure run lazily on first access |
Would love to get some feedback from those who adopted it widely to see how often this is an issue, whether we should try to adopt a syntax that always avoid the issue (e.g. something like @pzuraq suggested), or make that optional and something you do if you encounter the problem, etc |
Another thing to say about this is I think the non-decorator form does not suffer from the same issue: import Component from '@glimmer/component';
import { service } from 'ember-polaris-service';
import MyService from 'my-app/services/my-service';
export default class MyComponent extends Component {
myService = service(this, MyService);
} Overall, I personally prefer the decorator style, but if it is by a slim margin. Based on the current limitations in TypeScript and the current state of the decorator proposal, when using TypeScript the actual syntax required would be: import Component from '@glimmer/component';
import { service } from 'ember-polaris-service';
import MyService from 'my-app/services/my-service';
export default class MyComponent extends Component {
@service(() => MyService) accessor myService!: MyService;
} (Arguably Whereas – in the first snippet, that actually is the full TypeScript code because TypeScript can infer the type of the value. So if we have to add more syntax to the decorator style, the function version may just edge out in this use case. (@pzuraq some of this maybe useful real world feedback for decorators, especially regarding their inability to provide type information in TypeScript.) |
fwiw I don't believe that the inability to provide type info in TS is specifically because of the syntax or API, it should be possible to figure out what the type is in the future with the current proposal. Related issue: microsoft/TypeScript#52559 If I'm missing some context here I'd definitely be interested, I think being able to infer types from a decoration is very important. |
@pzuraq Yeah, certainly not an issue with the syntax itself, I think it's possible to design a TypeScript API for providing that information, but it just isn't there at the moment. I think this is an interesting case study that in theory would be a perfect fit for decorators, but after budgeting for the real world considerations, the non-decorator syntax end up being on par or arguably a bit more succinct. The TypeScript limitation is certainly a big part of it (it's unfortunate that even after typing all of those extra characters, you ended up with less safe code than the function version). But with the laziness/cycle issue factored in (say we adopt the closure syntax), and the Not exactly sure what to do with that information/feedback, but maybe just an additional datapoint for you when other related issues come up. |
anyone have a runnable example of this problem? I find the text / theory to be a little hard to follow, and some solid code in a stackblitz demo would be super helpful for me <3 |
Following what the readme says:
there are various reasons why services will depend on each other either deliberately or accidentally.
this leads to a js exception
ReferenceError: While generating link to route "content": can't access lexical declaration 'SharedApiService' before initialization
this issue came up from trying to model services from an existing application that has a setup like
auth -> shared_api -> internal_api -> auth (cycle)
The text was updated successfully, but these errors were encountered: