The Wayback Machine - https://web.archive.org/web/20211015103257/https://github.com/angular/angular/pull/43226
Skip to content
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

fix(compiler-cli): correctly interpret token arrays in @Injectable deps #43226

Closed
wants to merge 5 commits into from

Conversation

@JoostK
Copy link
Member

@JoostK JoostK commented Aug 22, 2021

When specifying the deps array in the @Injectable decorator to
inject dependencies into the injectable's factory function, it should
be possible to use an array literal to configure how the dependency
should be resolved by the DI system.

For example, the following example is allowed:

@Injectable({
  providedIn: 'root',
  useFactory: a => new AppService(a),
  deps: [[new Optional(), 'a']],
})
export class AppService {
  constructor(a) {}
}

Here, the 'a' string token should be injected as optional. However,
the AOT compiler incorrectly used the array literal itself as injection
token, resulting in a failure at runtime. Only if the token were to be
provided using [new Optional(), new Inject('a')] would it work
correctly.

This commit fixes the issue by using the last non-decorator in the
array literal as the token value, instead of the array literal itself.

Note that this is a loose interpretation of array literals: if a token
is omitted from the array literal then the array literal itself is used
as token, but any decorator such as new Optional() would still have
been applied. When there's multiple tokens in the list then only the
last one will be used as actual token, any prior tokens are silently
ignored. This behavior mirrors the JIT interpretation so is kept as is
for now, but may benefit from some stricter checking and better error
reporting in the future.

Fixes #42987


Thanks to @wszgrcy for their contribution.

@ngbot ngbot bot added this to the Backlog milestone Aug 22, 2021
@ngbot ngbot bot added this to the Backlog milestone Aug 22, 2021
@google-cla

This comment has been hidden.

@google-cla google-cla bot added the cla: no label Aug 22, 2021
@JoostK

This comment has been hidden.

@google-cla
Copy link

@google-cla google-cla bot commented Aug 22, 2021

All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter.

We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only @googlebot I consent. in this pull request.

Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the cla label to yes (if enabled on your project).

ℹ️ Googlers: Go here for more info.

@JoostK
Copy link
Member Author

@JoostK JoostK commented Aug 22, 2021

@wszgrcy I have added some fixup commits to your commit to get your contribution landed, but would need your explicit consent to get the google-cla bot happy.

@google-cla
Copy link

@google-cla google-cla bot commented Aug 22, 2021

All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter.

We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only @googlebot I consent. in this pull request.

Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the cla label to yes (if enabled on your project).

ℹ️ Googlers: Go here for more info.

@JoostK JoostK force-pushed the ngtsc/di-token-array branch from 78f9ca7 to ca8e65f Aug 23, 2021
@google-cla
Copy link

@google-cla google-cla bot commented Aug 23, 2021

All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter.

We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only @googlebot I consent. in this pull request.

Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the cla label to yes (if enabled on your project).

ℹ️ Googlers: Go here for more info.

@wszgrcy
Copy link
Contributor

@wszgrcy wszgrcy commented Aug 24, 2021

@googlebot I consent.

@google-cla google-cla bot added cla: yes and removed cla: no labels Aug 24, 2021
@JoostK JoostK marked this pull request as ready for review Aug 24, 2021
Copy link
Member

@petebacondarwin petebacondarwin left a comment

I kind of feel that we should be less relaxed about the ordering and duplication of tokens in such an array. If not an error then a warning, since it is unlikely that the developer meant to put more than one token into the array.

Apart from that this LGTM.

if (!isDecorator) {
meta.token = new WrappedNodeExpr(el);
Copy link
Member

@petebacondarwin petebacondarwin Sep 21, 2021

This means that the array could be of the form:

['a', new Optional()]

Is this desirable? Or should we require that the token comes after any decorators?

Copy link
Member Author

@JoostK JoostK Sep 24, 2021

This implements the same logic as the runtime; that doesn't necessarily mean that it's desirable but I wouldn't see a reason to disallow this.


@Injectable({
providedIn: 'root',
useFactory: () => new MyAlternateService(),
Copy link
Member

@petebacondarwin petebacondarwin Sep 21, 2021

Although this is correct it doesn't really show the intention of using a dependency in the factory.
Perhaps this should be:

Suggested change
useFactory: () => new MyAlternateService(),
useFactory: (dep: SomeDep|null) => new MyAlternateService(dep),

Copy link
Member Author

@JoostK JoostK Sep 24, 2021

Changed.

@AndrewKushnir
Copy link
Contributor

@AndrewKushnir AndrewKushnir commented Sep 23, 2021

@JoostK could you please rebase this PR when you get a chance? It looks like it was rebased a while ago and I can not start a presubmit because of that. Thank you.

wszgrcy and others added 4 commits Sep 24, 2021
…eps`

When specifying the `deps` array in the `@Injectable` decorator to
inject dependencies into the injectable's factory function, it should
be possible to use an array literal to configure how the dependency
should be resolved by the DI system.

For example, the following example is allowed:

```ts
@Injectable({
  providedIn: 'root',
  useFactory: a => new AppService(a),
  deps: [[new Optional(), 'a']],
})
export class AppService {
  constructor(a) {}
}
```

Here, the `'a'` string token should be injected as optional. However,
the AOT compiler incorrectly used the array literal itself as injection
token, resulting in a failure at runtime. Only if the token were to be
provided using `[new Optional(), new Inject('a')]` would it work
correctly.

This commit fixes the issue by using the last non-decorator in the
array literal as the token value, instead of the array literal itself.

Note that this is a loose interpretation of array literals: if a token
is omitted from the array literal then the array literal itself is used
as token, but any decorator such as `new Optional()` would still have
been applied. When there's multiple tokens in the list then only the
last one will be used as actual token, any prior tokens are silently
ignored. This behavior mirrors the JIT interpretation so is kept as is
for now, but may benefit from some stricter checking and better error
reporting in the future.

Fixes angular#42987
@JoostK JoostK force-pushed the ngtsc/di-token-array branch from ca8e65f to 6fec133 Sep 24, 2021
@JoostK
Copy link
Member Author

@JoostK JoostK commented Sep 24, 2021

I kind of feel that we should be less relaxed about the ordering and duplication of tokens in such an array. If not an error then a warning, since it is unlikely that the developer meant to put more than one token into the array.

I share your feeling that duplication of tokens should be reported as an error (see #42994 (comment) 😄), but strictly speaking that would be a breaking change and separate from this fix.

@AndrewKushnir
Copy link
Contributor

@AndrewKushnir AndrewKushnir commented Sep 28, 2021

alxhub added a commit that referenced this issue Sep 28, 2021
…eps` (#43226)

When specifying the `deps` array in the `@Injectable` decorator to
inject dependencies into the injectable's factory function, it should
be possible to use an array literal to configure how the dependency
should be resolved by the DI system.

For example, the following example is allowed:

```ts
@Injectable({
  providedIn: 'root',
  useFactory: a => new AppService(a),
  deps: [[new Optional(), 'a']],
})
export class AppService {
  constructor(a) {}
}
```

Here, the `'a'` string token should be injected as optional. However,
the AOT compiler incorrectly used the array literal itself as injection
token, resulting in a failure at runtime. Only if the token were to be
provided using `[new Optional(), new Inject('a')]` would it work
correctly.

This commit fixes the issue by using the last non-decorator in the
array literal as the token value, instead of the array literal itself.

Note that this is a loose interpretation of array literals: if a token
is omitted from the array literal then the array literal itself is used
as token, but any decorator such as `new Optional()` would still have
been applied. When there's multiple tokens in the list then only the
last one will be used as actual token, any prior tokens are silently
ignored. This behavior mirrors the JIT interpretation so is kept as is
for now, but may benefit from some stricter checking and better error
reporting in the future.

Fixes #42987

PR Close #43226
@alxhub alxhub closed this in 8d2b6af Sep 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

4 participants