From 39587ad127e9932347f5b456fe336bfb38ff9acb Mon Sep 17 00:00:00 2001 From: Andrius Date: Sat, 5 Oct 2019 15:18:05 +0300 Subject: [PATCH] fix(compiler-cli): resolve type of exported *ngIf variable. (#33016) Currently, method `getVarDeclarations()` does not try to resolve the type of exported variable from *ngIf directive. It always returns `any` type. By resolving the real type of exported variable, it is now possible to use this type information in language service and provide completions, go to definition and quick info functionality in expressions that use exported variable. Also language service will provide more accurate diagnostic errors during development. PR Close #33016 --- .../src/diagnostics/expression_diagnostics.ts | 13 +++++++++++++ .../diagnostics/expression_diagnostics_spec.ts | 12 ++++++++++++ packages/language-service/test/completions_spec.ts | 14 ++++++++++++++ 3 files changed, 39 insertions(+) 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 {