Skip to content

feat(compiler): narrow types of expressions used in *ngIf - #20702

Closed
chuckjaz wants to merge 1 commit into
angular:masterfrom
chuckjaz:type-narrow
Closed

chuckjaz wants to merge 1 commit into
angular:masterfrom
chuckjaz:type-narrow

Conversation

@chuckjaz

Copy link
Copy Markdown
Contributor

Structural directives can now specify a type guard that describes
what types can be inferred for an input expression inside the
directive's template.

NgIf was modified to declare an input guard on ngIf.

After this change, fullTemplateTypeCheck will infer that
usage of ngIf expression inside it's template is truthy.

For example, if a component has a property person?: Person
and a template of <div *ngIf="person"> {{person.name}} </div>
the compiler will no longer report that person might be null or
undefined.

The template compiler will generate code similar to,

  if (NgIf.ngIfTypeGuard(instance.person)) {
    instance.person.name
  }

to validate the template's use of the interpolation expression.
Calling the type guard in this fashion allows TypeScript to infer
that person is non-null.

PR Type

What kind of change does this PR introduce?

[x] Feature

What is the current behavior?

Given a component like:

export interface Person {
  name: string;
}

@Component({
  template: `<div *ngIf="person">Hello {{person.name}}</div>`
})
export Greeter {
  @Input() person?: Person;
}

The compiler would produce an error stating that person might be undefined.

What is the new behavior?

The compiler infers that person is Person inside the *ngIfbut is still Person | undefined outside the *ngIf.

Does this PR introduce a breaking change?

[ ] Yes
[ ] No

Other information

@trotyl

trotyl commented Nov 30, 2017

Copy link
Copy Markdown
Contributor

Likely will close #19756?

@mary-poppins

Copy link
Copy Markdown

You can preview 4d39227 at https://pr20702-4d39227.ngbuilds.io/.

@mhevery mhevery added the area: core Issues related to the framework runtime label Nov 30, 2017
@mary-poppins

Copy link
Copy Markdown

You can preview b4bdf80 at https://pr20702-b4bdf80.ngbuilds.io/.

@vicb vicb Nov 30, 2017 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guard*s*

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding this to the @Input() instead of doing name matching ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this with Misko and, though this is technically right, it give an esoteric feature way to much visibility and using naming convention is much better for such a corner case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q to discuss reduce vs foreach

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove = undefined ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let does not default to undefined but, rather, raises an error if it is accessed before initialization.

@vicb

vicb commented Nov 30, 2017

Copy link
Copy Markdown
Contributor

Comments:

  • would be nice to configure this via @Input(),
  • commit comment refers to ngIfTypeGuard instead of ngIfGuard,
  • needs doc updated - at least API docs,
  • pls add fixes #19756 as mentioned by @trotyl,
  • add a test with renamed inputs: @Input(a-dash) aDash;,
  • split the PR in 2 commits: general mechanism & NgIf update ?

@mary-poppins

Copy link
Copy Markdown

You can preview 2360d6e at https://pr20702-2360d6e.ngbuilds.io/.

@mary-poppins

Copy link
Copy Markdown

You can preview f7ef4a3 at https://pr20702-f7ef4a3.ngbuilds.io/.

@mary-poppins

Copy link
Copy Markdown

You can preview 4bc39c0 at https://pr20702-4bc39c0.ngbuilds.io/.

@mary-poppins

Copy link
Copy Markdown

You can preview 080e072 at https://pr20702-080e072.ngbuilds.io/.

@mary-poppins

Copy link
Copy Markdown

You can preview d9a3fa0 at https://pr20702-d9a3fa0.ngbuilds.io/.

@mary-poppins

Copy link
Copy Markdown

You can preview ce114a3 at https://pr20702-ce114a3.ngbuilds.io/.

@chuckjaz

chuckjaz commented Dec 6, 2017

Copy link
Copy Markdown
Contributor Author

would be nice to configure this via @input(),

As discussed off-line, this would raise the visibility of a very infrequently used feature.

commit comment refers to ngIfTypeGuard instead of ngIfGuard,

Fixed.

needs doc updated - at least API docs,

Will do in a follow-on PR.

pls add fixes #19756 as mentioned by @trotyl,

Done;

add a test with renamed inputs: @input(a-dash) aDash;,

Missed this one. Will add a test.

split the PR in 2 commits: general mechanism & NgIf update ?

I prefer to keep them together as they need to be taken together for the feature to work. For example, we would never revert one of the commits without reverting them all as the NgIf change makes no sense without the rest and vis versa.

Structural directives can now specify a type guard that describes
what types can be inferred for an input expression inside the
directive's template.

NgIf was modified to declare an input guard on ngIf.

After this change, `fullTemplateTypeCheck` will infer that
usage of `ngIf` expression inside it's template is truthy.

For example, if a component has a property `person?: Person`
and a template of `<div *ngIf="person"> {{person.name}} </div>`
the compiler will no longer report that `person` might be null or
undefined.

The template compiler will generate code similar to,

```
  if (NgIf.ngIfTypeGuard(instance.person)) {
    instance.person.name
  }
```

to validate the template's use of the interpolation expression.
Calling the type guard in this fashion allows TypeScript to infer
that `person` is non-null.

Fixes: angular#19756?
@chuckjaz

chuckjaz commented Dec 6, 2017

Copy link
Copy Markdown
Contributor Author

add a test with renamed inputs: @input(a-dash) aDash;,

Done.

@mary-poppins

Copy link
Copy Markdown

You can preview 4c47bc4 at https://pr20702-4c47bc4.ngbuilds.io/.

@chuckjaz chuckjaz added action: merge The PR is ready for merge by the caretaker target: major This PR is targeted for the next major release labels Dec 7, 2017
@jasonaden jasonaden closed this in e7d9cb3 Dec 8, 2017
@cyrilletuzi

Copy link
Copy Markdown
Contributor

@chuckjaz Does it close #18248 ?

@cyrilletuzi

Copy link
Copy Markdown
Contributor

@chuckjaz Can't get this to work. Angular 5.2.1, TS 2.5.3, fullTemplateTypeCheck enabled. Is there something else to do ?

@angular-automatic-lock-bot

Copy link
Copy Markdown

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot Bot locked and limited conversation to collaborators Sep 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime cla: yes target: major This PR is targeted for the next major release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants