{"action":"created","issue":{"url":"https://api.github.com/repos/angular/angular/issues/12398","repository_url":"https://api.github.com/repos/angular/angular","labels_url":"https://api.github.com/repos/angular/angular/issues/12398/labels{/name}","comments_url":"https://api.github.com/repos/angular/angular/issues/12398/comments","events_url":"https://api.github.com/repos/angular/angular/issues/12398/events","html_url":"https://github.com/angular/angular/issues/12398","id":184102179,"number":12398,"title":"Flow type information across *ngFor-like directives","user":{"login":"chuckjaz","id":372013,"avatar_url":"https://avatars1.githubusercontent.com/u/372013?v=4","gravatar_id":"","url":"https://api.github.com/users/chuckjaz","html_url":"https://github.com/chuckjaz","followers_url":"https://api.github.com/users/chuckjaz/followers","following_url":"https://api.github.com/users/chuckjaz/following{/other_user}","gists_url":"https://api.github.com/users/chuckjaz/gists{/gist_id}","starred_url":"https://api.github.com/users/chuckjaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chuckjaz/subscriptions","organizations_url":"https://api.github.com/users/chuckjaz/orgs","repos_url":"https://api.github.com/users/chuckjaz/repos","events_url":"https://api.github.com/users/chuckjaz/events{/privacy}","received_events_url":"https://api.github.com/users/chuckjaz/received_events","type":"User","site_admin":false},"labels":[{"id":375501288,"url":"https://api.github.com/repos/angular/angular/labels/comp:%20core%20&%20compiler","name":"comp: core & compiler","color":"c7def8","default":false},{"id":218063111,"url":"https://api.github.com/repos/angular/angular/labels/effort2:%20medium%20(days)","name":"effort2: medium (days)","color":"bfe5bf","default":false},{"id":221038051,"url":"https://api.github.com/repos/angular/angular/labels/flag:%20breaking%20change","name":"flag: breaking change","color":"d93f0b","default":false},{"id":375497245,"url":"https://api.github.com/repos/angular/angular/labels/freq3:%20high","name":"freq3: high","color":"fbca04","default":false},{"id":130606845,"url":"https://api.github.com/repos/angular/angular/labels/type:%20feature","name":"type: feature","color":"EEEEEE","default":false}],"state":"open","locked":false,"assignee":{"login":"chuckjaz","id":372013,"avatar_url":"https://avatars1.githubusercontent.com/u/372013?v=4","gravatar_id":"","url":"https://api.github.com/users/chuckjaz","html_url":"https://github.com/chuckjaz","followers_url":"https://api.github.com/users/chuckjaz/followers","following_url":"https://api.github.com/users/chuckjaz/following{/other_user}","gists_url":"https://api.github.com/users/chuckjaz/gists{/gist_id}","starred_url":"https://api.github.com/users/chuckjaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chuckjaz/subscriptions","organizations_url":"https://api.github.com/users/chuckjaz/orgs","repos_url":"https://api.github.com/users/chuckjaz/repos","events_url":"https://api.github.com/users/chuckjaz/events{/privacy}","received_events_url":"https://api.github.com/users/chuckjaz/received_events","type":"User","site_admin":false},"assignees":[{"login":"chuckjaz","id":372013,"avatar_url":"https://avatars1.githubusercontent.com/u/372013?v=4","gravatar_id":"","url":"https://api.github.com/users/chuckjaz","html_url":"https://github.com/chuckjaz","followers_url":"https://api.github.com/users/chuckjaz/followers","following_url":"https://api.github.com/users/chuckjaz/following{/other_user}","gists_url":"https://api.github.com/users/chuckjaz/gists{/gist_id}","starred_url":"https://api.github.com/users/chuckjaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chuckjaz/subscriptions","organizations_url":"https://api.github.com/users/chuckjaz/orgs","repos_url":"https://api.github.com/users/chuckjaz/repos","events_url":"https://api.github.com/users/chuckjaz/events{/privacy}","received_events_url":"https://api.github.com/users/chuckjaz/received_events","type":"User","site_admin":false}],"milestone":null,"comments":3,"created_at":"2016-10-19T23:48:16Z","updated_at":"2017-10-25T16:50:35Z","closed_at":null,"author_association":"MEMBER","body":"This issues supersedes and replaces the proposals in #10510 and #4850.\n# The problem\n\nAssume the following code example:\n\n``` ts\n@Component({\n  template: `\n  <ul>\n    <li *ngFor=\"let person of people; let i = index\">{{i + 0}}: {{person.last}}, {{person.first}}<li>\n  <ul>\n`\n})\nclass MyExample {\n  people: Person[] = [];\n}\n\nclass Person {\n  first: string;\n  last: string;\n}\n```\n\nIn Angular 2.1, the generated code for `person.first` is not type checked because the type of `person` is generated as `any`.  The type of `person` should be `Person`. This would also allow the Angular language service to supply `first` and `last` as completions for the expression `person.` in the interpolations.\n# Solution\n1. Change type of `NgFor` and `NgForRow` to take a type parameter `T`.\n2. Change type of `ngForOf` to be `T[] | Iterable<T>`. \n3. Change `$implicit` in `NgForRow` to be of type `T`.\n4. Modify the code generation to flow the type from the input bindings to the type parameters.\n\nThe modified `NgFor` and `NgForRow` would look like:\n\n``` ts\nexport class NgForRow<T> {\n  constructor(public $implicit: T, public index: number, public count: number) {}\n  ...\n}\n\n@Directive({selector: '[ngFor][ngForOf]'})\nexport class NgFor<T> implements DoCheck, OnChanges {\n  @Input() ngForOf: T[] ;\n  @Input() ngForTrackBy: TrackByFn;\n\n  private _differ: IterableDiffer = null;\n\n  constructor(\n      private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow<T>>,\n      private _differs: IterableDiffers, private _cdr: ChangeDetectorRef) {}\n\n  @Input()\n  set ngForTemplate(value: TemplateRef<NgForRow<T>>) {\n    if (value) {\n      this._template = value;\n    }\n  }\n\n ...\n}\n```\n\nHere is now a similar example from the above could be generated that flows the types from the input properties to the type parameters:\n\n``` ts\n// User Provided types\nclass Person {\n    first: string;\n    last:string;\n}\n\nclass MyComp {\n    items: string[];\n    people: Person[];\n}\n\n\n// Types representing relavant parts of the corresponding types in Angular\nclass AppView<T> {\n    context: T;\n}\n\ninterface TemplateRef<T> { create(context: T): ViewRef<T>; contextType: T }\ninterface ViewRef<T> { context: T }\n\n\nclass NgForRow<T> {\n    $implicit: T;\n    index: number;\n}\n\nclass NgFor<T>{\n    of: T[];\n    constructor(template: TemplateRef<NgForRow<T>>) { }\n}\n\n/**\n * Assume we have template such as this:\n * <div *ngFor=\"let person of people; let index = index\">\n *   {{index}}: {{person.first}}\n * </div>\n */\n\n// We can auto generate the following function which captures the\n// type relation between `of` property and `template`.\n\nfunction NgFor_Template<T>(of: T[]): TemplateRef<NgForRow<T>> { return undefined as any; }\nfunction NgFor_T<T>(of: T[]): T { return undefined as any; }\n\n// These global variables are here only to flow the type information.\nvar _comp: MyComp;\n\n// The template row can be constructed as:\nvar template = NgFor_Template(_comp && _comp.people);\nvar row: typeof template.contextType;\n\nclass RowView extends AppView<typeof row> {\n}\nvar rowView = new RowView();\n\n// Note the type of person is People\nvar person = rowView.context.$implicit;\n\n// The correct NgFor can be constructed as:\nvar p1 = NgFor_T(_comp && _comp.people);\nvar ngFor = new NgFor<typeof p1>(template);\n```\n\nTry it in the [TypeScript playground](http://www.typescriptlang.org/play/index.html#src=%2F%2F%20User%20Provided%20types%0D%0Aclass%20Person%20%7B%0D%0A%09first%3A%20string%3B%0D%0A%09last%3Astring%3B%0D%0A%7D%0D%0A%0D%0Aclass%20MyComp%20%7B%0D%0A%09items%3A%20string%5B%5D%3B%0D%0A%09people%3A%20Person%5B%5D%3B%0D%0A%7D%0D%0A%0D%0A%0D%0A%2F%2F%20Types%20representing%20relavant%20parts%20of%20the%20corresponding%20types%20in%20Angular%0D%0Aclass%20AppView%3CT%3E%20%7B%0D%0A%09context%3A%20T%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20TemplateRef%3CT%3E%20%7B%20create%28context%3A%20T%29%3A%20ViewRef%3CT%3E%3B%20contextType%3A%20T%20%7D%0D%0Ainterface%20ViewRef%3CT%3E%20%7B%20context%3A%20T%20%7D%0D%0A%0D%0A%0D%0Aclass%20NgForRow%3CT%3E%20%7B%0D%0A%09%24implicit%3A%20T%3B%0D%0A%09index%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Aclass%20NgFor%3CT%3E%7B%0D%0A%09of%3A%20T%5B%5D%3B%0D%0A%09constructor%28template%3A%20TemplateRef%3CNgForRow%3CT%3E%3E%29%20%7B%20%7D%0D%0A%7D%0D%0A%0D%0A%2F**%0D%0A%20*%20Assume%20we%20have%20template%20such%20as%20this%3A%0D%0A%20*%20%3Cdiv%20*ngFor%3D%22let%20person%20of%20people%3B%20let%20index%20%3D%20index%22%3E%0D%0A%20*%20%20%20%7B%7Bindex%7D%7D%3A%20%7B%7Bperson.first%7D%7D%0D%0A%20*%20%3C%2Fdiv%3E%0D%0A%20*%2F%0D%0A%0D%0A%2F%2F%20We%20can%20auto%20generate%20the%20following%20funciton%20which%20captures%20the%0D%0A%2F%2F%20type%20relation%20between%20%60of%60%20property%20and%20%60template%60.%0D%0A%0D%0Afunction%20NgFor_Template%3CT%3E%28of%3A%20T%5B%5D%29%3A%20TemplateRef%3CNgForRow%3CT%3E%3E%20%7B%20return%20undefined%20as%20any%3B%20%7D%0D%0Afunction%20NgFor_T%3CT%3E%28of%3A%20T%5B%5D%29%3A%20T%20%7B%20return%20undefined%20as%20any%3B%20%7D%0D%0A%0D%0A%2F%2F%20These%20global%20variables%20are%20here%20only%20to%20flow%20the%20type%20information.%0D%0Avar%20_comp%3A%20MyComp%3B%0D%0A%0D%0A%2F%2F%20The%20template%20row%20can%20be%20constructed%20as%3A%0D%0Avar%20template%20%3D%20NgFor_Template%28_comp%20%26%26%20_comp.people%29%3B%0D%0Avar%20row%3A%20typeof%20template.contextType%3B%0D%0A%0D%0Aclass%20RowView%20extends%20AppView%3Ctypeof%20row%3E%20%7B%0D%0A%7D%0D%0Avar%20rowView%20%3D%20new%20RowView%28%29%3B%0D%0A%0D%0A%2F%2F%20Note%20the%20type%20of%20person%20is%20People%0D%0Avar%20person%20%3D%20rowView.context.%24implicit%3B%0D%0A%0D%0A%2F%2F%20The%20correct%20NgFor%20can%20be%20constructed%20as%3A%0D%0Avar%20p1%20%3D%20NgFor_T%28_comp%20%26%26%20_comp.people%29%3B%0D%0Avar%20ngFor%20%3D%20new%20NgFor%3Ctypeof%20p1%3E%28template%29%3B%0D%0A).\n"},"comment":{"url":"https://api.github.com/repos/angular/angular/issues/comments/339395850","html_url":"https://github.com/angular/angular/issues/12398#issuecomment-339395850","issue_url":"https://api.github.com/repos/angular/angular/issues/12398","id":339395850,"user":{"login":"sgwatgit","id":1382768,"avatar_url":"https://avatars3.githubusercontent.com/u/1382768?v=4","gravatar_id":"","url":"https://api.github.com/users/sgwatgit","html_url":"https://github.com/sgwatgit","followers_url":"https://api.github.com/users/sgwatgit/followers","following_url":"https://api.github.com/users/sgwatgit/following{/other_user}","gists_url":"https://api.github.com/users/sgwatgit/gists{/gist_id}","starred_url":"https://api.github.com/users/sgwatgit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgwatgit/subscriptions","organizations_url":"https://api.github.com/users/sgwatgit/orgs","repos_url":"https://api.github.com/users/sgwatgit/repos","events_url":"https://api.github.com/users/sgwatgit/events{/privacy}","received_events_url":"https://api.github.com/users/sgwatgit/received_events","type":"User","site_admin":false},"created_at":"2017-10-25T16:50:35Z","updated_at":"2017-10-25T16:50:35Z","author_association":"NONE","body":"I'm wondering what is the current status of this feature?\r\n\r\nI'm using the following versions\r\n```\r\nAngular CLI: 1.5.0-rc.3                                     \r\nNode: 8.6.0                                                 \r\nOS: win32 x64                                               \r\nAngular: 5.0.0-rc.5                                         \r\n... animations, common, compiler, compiler-cli, core, forms \r\n... http, language-service, platform-browser                \r\n... platform-browser-dynamic, router                        \r\n                                                            \r\n@angular/cdk: 2.0.0-beta.12                                 \r\n@angular/cli: 1.5.0-rc.3                                    \r\n@angular/material: 2.0.0-beta.12                            \r\n@angular-devkit/build-optimizer: 0.0.31                     \r\n@angular-devkit/core: 0.0.20                                \r\n@angular-devkit/schematics: 0.0.34                          \r\n@ngtools/json-schema: 1.1.0                                 \r\n@ngtools/webpack: 1.8.0-rc.3                                \r\n@schematics/angular: 0.0.48                                 \r\ntypescript: 2.5.3                                           \r\nwebpack: 3.8.1                                              \r\n```\r\n\r\nIn my template I'm using `async` and a template variable but I lose typing support with the template variable.\r\n```\r\n<div *ngIf=\"(notes | async)?.length > 0; let unwrappedNotes; else emptyList\">\r\n  <mat-list-item *ngFor=\"let note of unwrappedNotes\">\r\n    <div class=\"date\">\r\n      <div class=\"month-day\">{{note.dateCreated | date: 'MM/dd'}}</div>\r\n    </div>\r\n    <div class=\"content\">\r\n      <h4 mat-line>{{note.title}}</h4>\r\n      <p mat-line>{{note.content}}</p>\r\n    </div>\r\n  </mat-list-item>\r\n</div>\r\n<ng-template #emptyList>\r\n  <mat-list-item>\r\n    <div *ngIf=\"!isLoading\" class=\"content\">No notes were found</div>\r\n  </mat-list-item>\r\n</ng-template>\r\n```\r\n\r\nHere `unwrappedNotes` has type any and therefore `note` has type any. If I rename `unwrappedNotes` to `notes`, the compiler is ok but I then lose typing support on `(notes | async)`.\r\n\r\nIt would be great to leverage all this great template syntax but if the typing support isn't there I'm more likely to not use the `async` pipe and instead manage my subscriptions within my component."}}
{"actor":{"display_login":"sgwatgit"}}