diff --git a/packages/language-service/src/typescript_host.ts b/packages/language-service/src/typescript_host.ts index aae10dd75c..7106f69f13 100644 --- a/packages/language-service/src/typescript_host.ts +++ b/packages/language-service/src/typescript_host.ts @@ -649,8 +649,14 @@ class TypeScriptSymbolQuery implements SymbolQuery { } getNonNullableType(symbol: Symbol): Symbol { - // TODO: Replace with typeChecker API when available; - return symbol; + if (symbol instanceof TypeWrapper && (typeof this.checker.getNonNullableType == 'function')) { + const tsType = symbol.tsType; + const nonNullableType = this.checker.getNonNullableType(tsType); + if (nonNullableType != tsType) { + return new TypeWrapper(nonNullableType, symbol.context); + } + } + return this.getBuiltinType(BuiltinType.Any); } getPipes(): SymbolTable { diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index eb9131f9ef..abb5d6c10b 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -95,7 +95,7 @@ describe('diagnostics', () => { const code = '\n@Component({template: \'
\'}) export class MyComponent {}'; addCode(code, (fileName, content) => { const diagnostics = ngService.getDiagnostics(fileName); - onlyModuleDiagnostics(diagnostics !); + expectOnlyModuleDiagnostics(diagnostics !); }); }); @@ -134,7 +134,7 @@ describe('diagnostics', () => { ` @Component({template: \`\`}) export class MyComponent { something: 'foo' | 'bar'; }`; addCode(code, fileName => { const diagnostics = ngService.getDiagnostics(fileName); - onlyModuleDiagnostics(diagnostics !); + expectOnlyModuleDiagnostics(diagnostics !); }); }); @@ -155,7 +155,7 @@ describe('diagnostics', () => { ` @Component({template: \`\`}) export class MyComponent { something = 'foo'; }})`; addCode(code, fileName => { const diagnostics = ngService.getDiagnostics(fileName); - onlyModuleDiagnostics(diagnostics !); + expectOnlyModuleDiagnostics(diagnostics !); }); }); @@ -233,7 +233,7 @@ describe('diagnostics', () => { }) export class MyComponent {} `, - fileName => onlyModuleDiagnostics(ngService.getDiagnostics(fileName))); + fileName => expectOnlyModuleDiagnostics(ngService.getDiagnostics(fileName))); }); // Issue #15625 @@ -254,10 +254,33 @@ describe('diagnostics', () => { `, fileName => { const diagnostics = ngService.getDiagnostics(fileName); - onlyModuleDiagnostics(diagnostics); + expectOnlyModuleDiagnostics(diagnostics); }); }); + // Issue #15885 + it('should be able to remove null and undefined from a type', () => { + mockHost.overrideOptions(options => { + options.strictNullChecks = true; + return options; + }); + addCode( + ` + @Component({ + selector: 'my-component', + template: \` {{test?.a}} + \` + }) + export class MyComponent { + test: {a: number, b: number} | null = { + a: 1, + b: 2 + }; + } + `, + fileName => expectOnlyModuleDiagnostics(ngService.getDiagnostics(fileName))); + }); + function addCode(code: string, cb: (fileName: string, content?: string) => void) { const fileName = '/app/app.component.ts'; const originalContent = mockHost.getFileContent(fileName); @@ -271,7 +294,7 @@ describe('diagnostics', () => { } } - function onlyModuleDiagnostics(diagnostics: Diagnostics) { + function expectOnlyModuleDiagnostics(diagnostics: Diagnostics) { // Expect only the 'MyComponent' diagnostic expect(diagnostics.length).toBe(1); if (diagnostics.length > 1) { diff --git a/packages/language-service/test/test_utils.ts b/packages/language-service/test/test_utils.ts index 380c652ad9..0cd46fcd0e 100644 --- a/packages/language-service/test/test_utils.ts +++ b/packages/language-service/test/test_utils.ts @@ -68,6 +68,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { private scriptVersion = new Map