diff --git a/packages/compiler-cli/src/diagnostics/expression_diagnostics.ts b/packages/compiler-cli/src/diagnostics/expression_diagnostics.ts index b00ef4659d..2253d2c077 100644 --- a/packages/compiler-cli/src/diagnostics/expression_diagnostics.ts +++ b/packages/compiler-cli/src/diagnostics/expression_diagnostics.ts @@ -154,6 +154,19 @@ function refinedVariableType( } } + // Special case the ngIf directive ( *ngIf="data$ | async as variable" ) + const ngIfDirective = + templateElement.directives.find(d => identifierName(d.directive.type) === 'NgIf'); + if (ngIfDirective) { + const ngIfBinding = ngIfDirective.inputs.find(i => i.directiveName === 'ngIf'); + if (ngIfBinding) { + const bindingType = new AstType(info.members, info.query, {}).getType(ngIfBinding.value); + if (bindingType) { + return bindingType; + } + } + } + // We can't do better, return any return info.query.getBuiltinType(BuiltinType.Any); } diff --git a/packages/compiler-cli/test/diagnostics/expression_diagnostics_spec.ts b/packages/compiler-cli/test/diagnostics/expression_diagnostics_spec.ts index 2b7c581407..9dcb990e92 100644 --- a/packages/compiler-cli/test/diagnostics/expression_diagnostics_spec.ts +++ b/packages/compiler-cli/test/diagnostics/expression_diagnostics_spec.ts @@ -130,6 +130,18 @@ describe('expression diagnostics', () => { `, 'Identifier \'nume\' is not defined')); + it('should accept an async *ngIf', () => accept(` +
+ {{p.name.first}} {{p.name.last}} +
+ `)); + it('should reject misspelled field in async *ngIf', () => reject( + ` +
+ {{p.name.first}} {{p.nume.last}} +
+ `, + 'Identifier \'nume\' is not defined')); it('should reject access to potentially undefined field', () => reject(`
{{maybe_person.name.first}}`, 'The expression might be null')); it('should accept a safe accss to an undefined field', diff --git a/packages/language-service/test/completions_spec.ts b/packages/language-service/test/completions_spec.ts index 2a374879b8..3a5e237020 100644 --- a/packages/language-service/test/completions_spec.ts +++ b/packages/language-service/test/completions_spec.ts @@ -55,6 +55,20 @@ describe('completions', () => { expectContains(fileName, 'name', 'name', 'street'); }); + it('should be able to get completions for exported *ngIf variable', () => { + const fileName = mockHost.addCode(` + interface Person { + name: string, + street: string + } + + @Component({template: '
{{person.~{name}name}} + }`); + expectContains(fileName, 'name', 'name', 'street'); + }); + it('should be able to infer the type of a ngForOf with an async pipe', () => { const fileName = mockHost.addCode(` interface Person {