fix(language-service): Update types for TypeScript nullability support

This commit is contained in:
Miško Hevery 2017-03-24 09:57:32 -07:00 committed by Hans
parent 6f5fccfeb7
commit 540581da3e
28 changed files with 234 additions and 225 deletions

View File

@ -111,7 +111,7 @@ class _Visitor implements html.Visitor {
this._translations = translations; this._translations = translations;
// Construct a single fake root element // Construct a single fake root element
const wrapper = new html.Element('wrapper', [], nodes, undefined, undefined, undefined); const wrapper = new html.Element('wrapper', [], nodes, undefined !, undefined, undefined);
const translatedNode = wrapper.visit(this, null); const translatedNode = wrapper.visit(this, null);

View File

@ -9,12 +9,12 @@
import {ParseSourceSpan} from '../parse_util'; import {ParseSourceSpan} from '../parse_util';
export interface Node { export interface Node {
sourceSpan: ParseSourceSpan|null; sourceSpan: ParseSourceSpan;
visit(visitor: Visitor, context: any): any; visit(visitor: Visitor, context: any): any;
} }
export class Text implements Node { export class Text implements Node {
constructor(public value: string, public sourceSpan: ParseSourceSpan|null) {} constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: Visitor, context: any): any { return visitor.visitText(this, context); } visit(visitor: Visitor, context: any): any { return visitor.visitText(this, context); }
} }
@ -43,8 +43,7 @@ export class Attribute implements Node {
export class Element implements Node { export class Element implements Node {
constructor( constructor(
public name: string, public attrs: Attribute[], public children: Node[], public name: string, public attrs: Attribute[], public children: Node[],
public sourceSpan: ParseSourceSpan|null = null, public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null = null,
public startSourceSpan: ParseSourceSpan|null = null,
public endSourceSpan: ParseSourceSpan|null = null) {} public endSourceSpan: ParseSourceSpan|null = null) {}
visit(visitor: Visitor, context: any): any { return visitor.visitElement(this, context); } visit(visitor: Visitor, context: any): any { return visitor.visitElement(this, context); }
} }

View File

@ -19,7 +19,7 @@ export interface TemplateAst {
/** /**
* The source span from which this node was parsed. * The source span from which this node was parsed.
*/ */
sourceSpan: ParseSourceSpan|null; sourceSpan: ParseSourceSpan;
/** /**
* Visit this node and possibly transform it. * Visit this node and possibly transform it.
@ -127,7 +127,7 @@ export class ElementAst implements TemplateAst {
public directives: DirectiveAst[], public providers: ProviderAst[], public directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public queryMatches: QueryMatch[], public hasViewContainer: boolean, public queryMatches: QueryMatch[],
public children: TemplateAst[], public ngContentIndex: number|null, public children: TemplateAst[], public ngContentIndex: number|null,
public sourceSpan: ParseSourceSpan|null, public endSourceSpan: ParseSourceSpan|null) {} public sourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan|null) {}
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElement(this, context); return visitor.visitElement(this, context);

View File

@ -391,7 +391,7 @@ class TemplateParseVisitor implements html.Visitor {
nodeName, attrs, elementProps, events, references, nodeName, attrs, elementProps, events, references,
providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, providerContext.queryMatches, children, providerContext.transformedHasViewContainer, providerContext.queryMatches, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan || null, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan,
element.endSourceSpan || null); element.endSourceSpan || null);
} }

View File

@ -25,5 +25,5 @@ export class AstPath<T> {
push(node: T) { this.path.push(node); } push(node: T) { this.path.push(node); }
pop(): T { return this.path.pop(); } pop(): T { return this.path.pop() !; }
} }

View File

@ -29,27 +29,27 @@ const hiddenHtmlElements = {
link: true, link: true,
}; };
export function getTemplateCompletions(templateInfo: TemplateInfo): Completions { export function getTemplateCompletions(templateInfo: TemplateInfo): Completions|undefined {
let result: Completions = undefined; let result: Completions|undefined = undefined;
let {htmlAst, templateAst, template} = templateInfo; let {htmlAst, templateAst, template} = templateInfo;
// The templateNode starts at the delimiter character so we add 1 to skip it. // The templateNode starts at the delimiter character so we add 1 to skip it.
let templatePosition = templateInfo.position - template.span.start; let templatePosition = templateInfo.position ! - template.span.start;
let path = new HtmlAstPath(htmlAst, templatePosition); let path = new HtmlAstPath(htmlAst, templatePosition);
let mostSpecific = path.tail; let mostSpecific = path.tail;
if (path.empty) { if (path.empty) {
result = elementCompletions(templateInfo, path); result = elementCompletions(templateInfo, path);
} else { } else {
let astPosition = templatePosition - mostSpecific.sourceSpan.start.offset; let astPosition = templatePosition - mostSpecific !.sourceSpan !.start.offset;
mostSpecific.visit( mostSpecific !.visit(
{ {
visitElement(ast) { visitElement(ast) {
let startTagSpan = spanOf(ast.sourceSpan); let startTagSpan = spanOf(ast.sourceSpan);
let tagLen = ast.name.length; let tagLen = ast.name.length;
if (templatePosition <= if (templatePosition <=
startTagSpan.start + tagLen + 1 /* 1 for the opening angle bracked */) { startTagSpan !.start + tagLen + 1 /* 1 for the opening angle bracked */) {
// If we are in the tag then return the element completions. // If we are in the tag then return the element completions.
result = elementCompletions(templateInfo, path); result = elementCompletions(templateInfo, path);
} else if (templatePosition < startTagSpan.end) { } else if (templatePosition < startTagSpan !.end) {
// We are in the attribute section of the element (but not in an attribute). // We are in the attribute section of the element (but not in an attribute).
// Return the attribute completions. // Return the attribute completions.
result = attributeCompletions(templateInfo, path); result = attributeCompletions(templateInfo, path);
@ -65,7 +65,7 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions
}, },
visitText(ast) { visitText(ast) {
// Check if we are in a entity. // Check if we are in a entity.
result = entityCompletions(getSourceText(template, spanOf(ast)), astPosition); result = entityCompletions(getSourceText(template, spanOf(ast) !), astPosition);
if (result) return result; if (result) return result;
result = interpolationCompletions(templateInfo, templatePosition); result = interpolationCompletions(templateInfo, templatePosition);
if (result) return result; if (result) return result;
@ -96,8 +96,8 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions
return result; return result;
} }
function attributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions { function attributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|undefined {
let item = path.tail instanceof Element ? path.tail : path.parentOf(path.tail); let item = path.tail instanceof Element ? path.tail : path.parentOf(path.tail !);
if (item instanceof Element) { if (item instanceof Element) {
return attributeCompletionsForElement(info, item.name, item); return attributeCompletionsForElement(info, item.name, item);
} }
@ -146,7 +146,7 @@ function getAttributeInfosForElement(
const selectorAndAttributeNames = const selectorAndAttributeNames =
applicableSelectors.map(selector => ({selector, attrs: selector.attrs.filter(a => !!a)})); applicableSelectors.map(selector => ({selector, attrs: selector.attrs.filter(a => !!a)}));
let attrs = flatten(selectorAndAttributeNames.map<AttrInfo[]>(selectorAndAttr => { let attrs = flatten(selectorAndAttributeNames.map<AttrInfo[]>(selectorAndAttr => {
const directive = selectorMap.get(selectorAndAttr.selector); const directive = selectorMap.get(selectorAndAttr.selector) !;
const result = selectorAndAttr.attrs.map<AttrInfo>( const result = selectorAndAttr.attrs.map<AttrInfo>(
name => ({name, input: name in directive.inputs, output: name in directive.outputs})); name => ({name, input: name in directive.inputs, output: name in directive.outputs}));
return result; return result;
@ -165,7 +165,7 @@ function getAttributeInfosForElement(
// All input and output properties of the matching directives should be added. // All input and output properties of the matching directives should be added.
let elementSelector = element ? let elementSelector = element ?
createElementCssSelector(element) : createElementCssSelector(element) :
createElementCssSelector(new Element(elementName, [], [], undefined, undefined, undefined)); createElementCssSelector(new Element(elementName, [], [], null !, null, null));
let matcher = new SelectorMatcher(); let matcher = new SelectorMatcher();
matcher.addSelectables(selectors); matcher.addSelectables(selectors);
@ -188,7 +188,7 @@ function getAttributeInfosForElement(
} }
function attributeValueCompletions( function attributeValueCompletions(
info: TemplateInfo, position: number, attr: Attribute): Completions { info: TemplateInfo, position: number, attr: Attribute): Completions|undefined {
const path = new TemplateAstPath(info.templateAst, position); const path = new TemplateAstPath(info.templateAst, position);
const mostSpecific = path.tail; const mostSpecific = path.tail;
if (mostSpecific) { if (mostSpecific) {
@ -209,7 +209,7 @@ function attributeValueCompletions(
} }
} }
function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions { function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|undefined {
let htmlNames = elementNames().filter(name => !(name in hiddenHtmlElements)); let htmlNames = elementNames().filter(name => !(name in hiddenHtmlElements));
// Collect the elements referenced by the selectors // Collect the elements referenced by the selectors
@ -217,18 +217,18 @@ function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions
getSelectors(info).selectors.map(selector => selector.element).filter(name => !!name); getSelectors(info).selectors.map(selector => selector.element).filter(name => !!name);
let components = let components =
directiveElements.map<Completion>(name => ({kind: 'component', name: name, sort: name})); directiveElements.map<Completion>(name => ({kind: 'component', name: name !, sort: name !}));
let htmlElements = htmlNames.map<Completion>(name => ({kind: 'element', name: name, sort: name})); let htmlElements = htmlNames.map<Completion>(name => ({kind: 'element', name: name, sort: name}));
// Return components and html elements // Return components and html elements
return uniqueByName(htmlElements.concat(components)); return uniqueByName(htmlElements.concat(components));
} }
function entityCompletions(value: string, position: number): Completions { function entityCompletions(value: string, position: number): Completions|undefined {
// Look for entity completions // Look for entity completions
const re = /&[A-Za-z]*;?(?!\d)/g; const re = /&[A-Za-z]*;?(?!\d)/g;
let found: RegExpExecArray|null; let found: RegExpExecArray|null;
let result: Completions; let result: Completions|undefined = undefined;
while (found = re.exec(value)) { while (found = re.exec(value)) {
let len = found[0].length; let len = found[0].length;
if (position >= found.index && position < (found.index + len)) { if (position >= found.index && position < (found.index + len)) {
@ -240,7 +240,7 @@ function entityCompletions(value: string, position: number): Completions {
return result; return result;
} }
function interpolationCompletions(info: TemplateInfo, position: number): Completions { function interpolationCompletions(info: TemplateInfo, position: number): Completions|undefined {
// Look for an interpolation in at the position. // Look for an interpolation in at the position.
const templatePath = new TemplateAstPath(info.templateAst, position); const templatePath = new TemplateAstPath(info.templateAst, position);
const mostSpecific = templatePath.tail; const mostSpecific = templatePath.tail;
@ -258,13 +258,14 @@ function interpolationCompletions(info: TemplateInfo, position: number): Complet
// the attributes of an "a" element, not requesting completion in the a text element. This // the attributes of an "a" element, not requesting completion in the a text element. This
// code checks for this case and returns element completions if it is detected or undefined // code checks for this case and returns element completions if it is detected or undefined
// if it is not. // if it is not.
function voidElementAttributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions { function voidElementAttributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|
undefined {
let tail = path.tail; let tail = path.tail;
if (tail instanceof Text) { if (tail instanceof Text) {
let match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/); let match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/) !;
// The position must be after the match, otherwise we are still in a place where elements // The position must be after the match, otherwise we are still in a place where elements
// are expected (such as `<|a` or `<a|`; we only want attributes for `<a |` or after). // are expected (such as `<|a` or `<a|`; we only want attributes for `<a |` or after).
if (match && path.position >= match.index + match[0].length + tail.sourceSpan.start.offset) { if (match && path.position >= match.index ! + match[0].length + tail.sourceSpan.start.offset) {
return attributeCompletionsForElement(info, match[3]); return attributeCompletionsForElement(info, match[3]);
} }
} }
@ -310,7 +311,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
this.info.expressionParser.parseTemplateBindings(key, this.attr.value, null); this.info.expressionParser.parseTemplateBindings(key, this.attr.value, null);
// find the template binding that contains the position // find the template binding that contains the position
const valueRelativePosition = this.position - this.attr.valueSpan.start.offset - 1; const valueRelativePosition = this.position - this.attr.valueSpan !.start.offset - 1;
const bindings = templateBindingResult.templateBindings; const bindings = templateBindingResult.templateBindings;
const binding = const binding =
bindings.find( bindings.find(
@ -340,7 +341,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
// template reference's type parameter. // template reference's type parameter.
const directiveMetadata = selectorInfo.map.get(selector); const directiveMetadata = selectorInfo.map.get(selector);
const contextTable = const contextTable =
this.info.template.query.getTemplateContext(directiveMetadata.type.reference); this.info.template.query.getTemplateContext(directiveMetadata !.type.reference);
if (contextTable) { if (contextTable) {
this.result = this.symbolsToCompletions(contextTable.values()); this.result = this.symbolsToCompletions(contextTable.values());
} }
@ -370,7 +371,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
const expressionPosition = this.position - ast.sourceSpan.start.offset; const expressionPosition = this.position - ast.sourceSpan.start.offset;
if (inSpan(expressionPosition, ast.value.span)) { if (inSpan(expressionPosition, ast.value.span)) {
const completions = getExpressionCompletions( const completions = getExpressionCompletions(
this.getExpressionScope(), ast.value, expressionPosition, this.info.template.query); this.getExpressionScope !(), ast.value, expressionPosition, this.info.template.query);
if (completions) { if (completions) {
this.result = this.symbolsToCompletions(completions); this.result = this.symbolsToCompletions(completions);
} }
@ -379,8 +380,8 @@ class ExpressionVisitor extends NullTemplateVisitor {
private attributeValueCompletions(value: AST, position?: number) { private attributeValueCompletions(value: AST, position?: number) {
const symbols = getExpressionCompletions( const symbols = getExpressionCompletions(
this.getExpressionScope(), value, position == null ? this.attributeValuePosition : position, this.getExpressionScope !(), value,
this.info.template.query); position == null ? this.attributeValuePosition : position, this.info.template.query);
if (symbols) { if (symbols) {
this.result = this.symbolsToCompletions(symbols); this.result = this.symbolsToCompletions(symbols);
} }
@ -392,7 +393,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
} }
private get attributeValuePosition() { private get attributeValuePosition() {
return this.position - this.attr.valueSpan.start.offset - 1; return this.position - this.attr !.valueSpan !.start.offset - 1;
} }
} }

View File

@ -29,7 +29,7 @@ export function getTemplateDiagnostics(
results.push(...ast.parseErrors.map<Diagnostic>( results.push(...ast.parseErrors.map<Diagnostic>(
e => ({ e => ({
kind: DiagnosticKind.Error, kind: DiagnosticKind.Error,
span: offsetSpan(spanOf(e.span), template.span.start), span: offsetSpan(spanOf(e.span) !, template.span.start),
message: e.msg message: e.msg
}))); })));
} else if (ast.templateAst) { } else if (ast.templateAst) {
@ -66,7 +66,8 @@ export function getDeclarationDiagnostics(
report( report(
`Component '${declaration.type.name}' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration`); `Component '${declaration.type.name}' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration`);
} }
if (!declaration.metadata.template.template && !declaration.metadata.template.templateUrl) { if (!declaration.metadata.template !.template &&
!declaration.metadata.template !.templateUrl) {
report(`Component ${declaration.type.name} must have a template or templateUrl`); report(`Component ${declaration.type.name} must have a template or templateUrl`);
} }
} else { } else {
@ -74,7 +75,7 @@ export function getDeclarationDiagnostics(
directives = new Set(); directives = new Set();
modules.ngModules.forEach(module => { modules.ngModules.forEach(module => {
module.declaredDirectives.forEach( module.declaredDirectives.forEach(
directive => { directives.add(directive.reference); }); directive => { directives !.add(directive.reference); });
}); });
} }
if (!directives.has(declaration.type)) { if (!directives.has(declaration.type)) {
@ -92,17 +93,17 @@ function getTemplateExpressionDiagnostics(
template: TemplateSource, astResult: AstResult): Diagnostics { template: TemplateSource, astResult: AstResult): Diagnostics {
const info: TemplateInfo = { const info: TemplateInfo = {
template, template,
htmlAst: astResult.htmlAst, htmlAst: astResult.htmlAst !,
directive: astResult.directive, directive: astResult.directive !,
directives: astResult.directives, directives: astResult.directives !,
pipes: astResult.pipes, pipes: astResult.pipes !,
templateAst: astResult.templateAst, templateAst: astResult.templateAst !,
expressionParser: astResult.expressionParser expressionParser: astResult.expressionParser !
}; };
const visitor = new ExpressionDiagnosticsVisitor( const visitor = new ExpressionDiagnosticsVisitor(
info, (path: TemplateAstPath, includeEvent: boolean) => info, (path: TemplateAstPath, includeEvent: boolean) =>
getExpressionScope(info, path, includeEvent)); getExpressionScope(info, path, includeEvent));
templateVisitAll(visitor, astResult.templateAst); templateVisitAll(visitor, astResult.templateAst !);
return visitor.diagnostics; return visitor.diagnostics;
} }
@ -153,15 +154,15 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
visitVariable(ast: VariableAst): void { visitVariable(ast: VariableAst): void {
const directive = this.directiveSummary; const directive = this.directiveSummary;
if (directive && ast.value) { if (directive && ast.value) {
const context = this.info.template.query.getTemplateContext(directive.type.reference); const context = this.info.template.query.getTemplateContext(directive.type.reference) !;
if (context && !context.has(ast.value)) { if (context && !context.has(ast.value)) {
if (ast.value === '$implicit') { if (ast.value === '$implicit') {
this.reportError( this.reportError(
'The template context does not have an implicit value', spanOf(ast.sourceSpan)); 'The template context does not have an implicit value', spanOf(ast.sourceSpan) !);
} else { } else {
this.reportError( this.reportError(
`The template context does not defined a member called '${ast.value}'`, `The template context does not defined a member called '${ast.value}'`,
spanOf(ast.sourceSpan)); spanOf(ast.sourceSpan) !);
} }
} }
} }
@ -180,7 +181,7 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
// Find directive that refernces this template // Find directive that refernces this template
this.directiveSummary = this.directiveSummary =
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type)); ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type)) !;
// Process children // Process children
super.visitEmbeddedTemplate(ast, context); super.visitEmbeddedTemplate(ast, context);
@ -225,7 +226,7 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
return result; return result;
} }
private findElement(position: number): Element { private findElement(position: number): Element|undefined {
const htmlPath = new HtmlAstPath(this.info.htmlAst, position); const htmlPath = new HtmlAstPath(this.info.htmlAst, position);
if (htmlPath.tail instanceof Element) { if (htmlPath.tail instanceof Element) {
return htmlPath.tail; return htmlPath.tail;

View File

@ -25,10 +25,10 @@ export function getExpressionDiagnostics(
} }
export function getExpressionCompletions( export function getExpressionCompletions(
scope: SymbolTable, ast: AST, position: number, query: SymbolQuery): Symbol[] { scope: SymbolTable, ast: AST, position: number, query: SymbolQuery): Symbol[]|undefined {
const path = new AstPath(ast, position); const path = new AstPath(ast, position);
if (path.empty) return undefined; if (path.empty) return undefined;
const tail = path.tail; const tail = path.tail !;
let result: SymbolTable|undefined = scope; let result: SymbolTable|undefined = scope;
function getType(ast: AST): Symbol { return new AstType(scope, query, {}).getType(ast); } function getType(ast: AST): Symbol { return new AstType(scope, query, {}).getType(ast); }
@ -84,15 +84,15 @@ export function getExpressionCompletions(
export function getExpressionSymbol( export function getExpressionSymbol(
scope: SymbolTable, ast: AST, position: number, scope: SymbolTable, ast: AST, position: number,
query: SymbolQuery): {symbol: Symbol, span: Span} { query: SymbolQuery): {symbol: Symbol, span: Span}|undefined {
const path = new AstPath(ast, position, /* excludeEmpty */ true); const path = new AstPath(ast, position, /* excludeEmpty */ true);
if (path.empty) return undefined; if (path.empty) return undefined;
const tail = path.tail; const tail = path.tail !;
function getType(ast: AST): Symbol { return new AstType(scope, query, {}).getType(ast); } function getType(ast: AST): Symbol { return new AstType(scope, query, {}).getType(ast); }
let symbol: Symbol = undefined; let symbol: Symbol|undefined = undefined;
let span: Span = undefined; let span: Span|undefined = undefined;
// If the completion request is in a not in a pipe or property access then the global scope // If the completion request is in a not in a pipe or property access then the global scope
// (that is the scope of the implicit receiver) is the right scope as the user is typing the // (that is the scope of the implicit receiver) is the right scope as the user is typing the
@ -340,7 +340,7 @@ class AstType implements ExpressionVisitor {
// support contextual typing of arguments so this is simpler than TypeScript's // support contextual typing of arguments so this is simpler than TypeScript's
// version. // version.
const args = ast.args.map(arg => this.getType(arg)); const args = ast.args.map(arg => this.getType(arg));
const target = this.getType(ast.target); const target = this.getType(ast.target !);
if (!target || !target.callable) return this.reportError('Call target is not callable', ast); if (!target || !target.callable) return this.reportError('Call target is not callable', ast);
const signature = target.selectSignature(args); const signature = target.selectSignature(args);
if (signature) return signature.result; if (signature) return signature.result;
@ -434,7 +434,7 @@ class AstType implements ExpressionVisitor {
// The type of a pipe node is the return type of the pipe's transform method. The table returned // The type of a pipe node is the return type of the pipe's transform method. The table returned
// by getPipes() is expected to contain symbols with the corresponding transform method type. // by getPipes() is expected to contain symbols with the corresponding transform method type.
const pipe = this.query.getPipes().get(ast.name); const pipe = this.query.getPipes().get(ast.name);
if (!pipe) return this.reportError(`No pipe by the name ${pipe.name} found`, ast); if (!pipe) return this.reportError(`No pipe by the name ${ast.name} found`, ast);
const expType = this.getType(ast.exp); const expType = this.getType(ast.exp);
const signature = const signature =
pipe.selectSignature([expType].concat(ast.args.map(arg => this.getType(arg)))); pipe.selectSignature([expType].concat(ast.args.map(arg => this.getType(arg))));
@ -495,8 +495,8 @@ class AstType implements ExpressionVisitor {
// The type of a method is the selected methods result type. // The type of a method is the selected methods result type.
const method = receiverType.members().get(ast.name); const method = receiverType.members().get(ast.name);
if (!method) return this.reportError(`Unknown method ${ast.name}`, ast); if (!method) return this.reportError(`Unknown method ${ast.name}`, ast);
if (!method.type.callable) return this.reportError(`Member ${ast.name} is not callable`, ast); if (!method.type !.callable) return this.reportError(`Member ${ast.name} is not callable`, ast);
const signature = method.type.selectSignature(ast.args.map(arg => this.getType(arg))); const signature = method.type !.selectSignature(ast.args.map(arg => this.getType(arg)));
if (!signature) if (!signature)
return this.reportError(`Unable to resolve signature for call of method ${ast.name}`, ast); return this.reportError(`Unable to resolve signature for call of method ${ast.name}`, ast);
return signature.result; return signature.result;
@ -550,7 +550,7 @@ class AstType implements ExpressionVisitor {
private isAny(symbol: Symbol): boolean { private isAny(symbol: Symbol): boolean {
return !symbol || this.query.getTypeKind(symbol) == BuiltinType.Any || return !symbol || this.query.getTypeKind(symbol) == BuiltinType.Any ||
(symbol.type && this.isAny(symbol.type)); (!!symbol.type && this.isAny(symbol.type));
} }
} }
@ -601,7 +601,7 @@ function visitChildren(ast: AST, visitor: ExpressionVisitor) {
visit(ast.falseExp); visit(ast.falseExp);
}, },
visitFunctionCall(ast) { visitFunctionCall(ast) {
visit(ast.target); visit(ast.target !);
visitAll(ast.args); visitAll(ast.args);
}, },
visitImplicitReceiver(ast) {}, visitImplicitReceiver(ast) {},
@ -676,7 +676,7 @@ function getReferences(info: TemplateInfo): SymbolDeclaration[] {
function processReferences(references: ReferenceAst[]) { function processReferences(references: ReferenceAst[]) {
for (const reference of references) { for (const reference of references) {
let type: Symbol; let type: Symbol = undefined !;
if (reference.value) { if (reference.value) {
type = info.template.query.getTypeSymbol(tokenReference(reference.value)); type = info.template.query.getTypeSymbol(tokenReference(reference.value));
} }
@ -721,11 +721,11 @@ function getVarDeclarations(info: TemplateInfo, path: TemplateAstPath): SymbolDe
.find(c => !!c); .find(c => !!c);
// Determine the type of the context field referenced by variable.value. // Determine the type of the context field referenced by variable.value.
let type: Symbol; let type: Symbol = undefined !;
if (context) { if (context) {
const value = context.get(variable.value); const value = context.get(variable.value);
if (value) { if (value) {
type = value.type; type = value.type !;
let kind = info.template.query.getTypeKind(type); let kind = info.template.query.getTypeKind(type);
if (kind === BuiltinType.Any || kind == BuiltinType.Unbound) { if (kind === BuiltinType.Any || kind == BuiltinType.Unbound) {
// The any type is not very useful here. For special cases, such as ngFor, we can do // The any type is not very useful here. For special cases, such as ngFor, we can do
@ -762,7 +762,7 @@ function refinedVariableType(
const bindingType = const bindingType =
new AstType(info.template.members, info.template.query, {}).getType(ngForOfBinding.value); new AstType(info.template.members, info.template.query, {}).getType(ngForOfBinding.value);
if (bindingType) { if (bindingType) {
return info.template.query.getElementType(bindingType); return info.template.query.getElementType(bindingType) !;
} }
} }
} }
@ -771,7 +771,7 @@ function refinedVariableType(
return type; return type;
} }
function getDefintionOf(info: TemplateInfo, ast: TemplateAst): Definition { function getDefintionOf(info: TemplateInfo, ast: TemplateAst): Definition|undefined {
if (info.fileName) { if (info.fileName) {
const templateOffset = info.template.span.start; const templateOffset = info.template.span.start;
return [{ return [{

View File

@ -10,7 +10,7 @@ import {TemplateInfo} from './common';
import {locateSymbol} from './locate_symbol'; import {locateSymbol} from './locate_symbol';
import {Hover, HoverTextSection, Symbol} from './types'; import {Hover, HoverTextSection, Symbol} from './types';
export function getHover(info: TemplateInfo): Hover { export function getHover(info: TemplateInfo): Hover|undefined {
const result = locateSymbol(info); const result = locateSymbol(info);
if (result) { if (result) {
return {text: hoverTextOf(result.symbol), span: result.span}; return {text: hoverTextOf(result.symbol), span: result.span};

View File

@ -59,7 +59,7 @@ class HtmlAstPathBuilder extends ChildVisitor {
constructor(private position: number) { super(); } constructor(private position: number) { super(); }
visit(ast: Node, context: any): any { visit(ast: Node, context: any): any {
let span = spanOf(ast); let span = spanOf(ast as any);
if (inSpan(this.position, span)) { if (inSpan(this.position, span)) {
this.path.push(ast); this.path.push(ast);
} else { } else {

View File

@ -32,7 +32,7 @@ class LanguageServiceImpl implements LanguageService {
getTemplateReferences(): string[] { return this.host.getTemplateReferences(); } getTemplateReferences(): string[] { return this.host.getTemplateReferences(); }
getDiagnostics(fileName: string): Diagnostics { getDiagnostics(fileName: string): Diagnostics|undefined {
let results: Diagnostics = []; let results: Diagnostics = [];
let templates = this.host.getTemplates(fileName); let templates = this.host.getTemplates(fileName);
if (templates && templates.length) { if (templates && templates.length) {
@ -70,14 +70,14 @@ class LanguageServiceImpl implements LanguageService {
} }
} }
getHoverAt(fileName: string, position: number): Hover { getHoverAt(fileName: string, position: number): Hover|undefined {
let templateInfo = this.getTemplateAstAtPosition(fileName, position); let templateInfo = this.getTemplateAstAtPosition(fileName, position);
if (templateInfo) { if (templateInfo) {
return getHover(templateInfo); return getHover(templateInfo);
} }
} }
private getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo { private getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo|undefined {
let template = this.host.getTemplateAt(fileName, position); let template = this.host.getTemplateAt(fileName, position);
if (template) { if (template) {
let astResult = this.getTemplateAst(template, fileName); let astResult = this.getTemplateAst(template, fileName);
@ -87,18 +87,18 @@ class LanguageServiceImpl implements LanguageService {
fileName, fileName,
template, template,
htmlAst: astResult.htmlAst, htmlAst: astResult.htmlAst,
directive: astResult.directive, directive: astResult.directive !,
directives: astResult.directives, directives: astResult.directives !,
pipes: astResult.pipes, pipes: astResult.pipes !,
templateAst: astResult.templateAst, templateAst: astResult.templateAst,
expressionParser: astResult.expressionParser expressionParser: astResult.expressionParser !
}; };
} }
return undefined; return undefined;
} }
getTemplateAst(template: TemplateSource, contextFile: string): AstResult { getTemplateAst(template: TemplateSource, contextFile: string): AstResult {
let result: AstResult; let result: AstResult = undefined !;
try { try {
const resolvedMetadata = const resolvedMetadata =
this.metadataResolver.getNonNormalizedDirectiveMetadata(template.type as any); this.metadataResolver.getNonNormalizedDirectiveMetadata(template.type as any);
@ -109,10 +109,10 @@ class LanguageServiceImpl implements LanguageService {
const expressionParser = new Parser(new Lexer()); const expressionParser = new Parser(new Lexer());
const config = new CompilerConfig(); const config = new CompilerConfig();
const parser = new TemplateParser( const parser = new TemplateParser(
config, expressionParser, new DomElementSchemaRegistry(), htmlParser, null, []); config, expressionParser, new DomElementSchemaRegistry(), htmlParser, null !, []);
const htmlResult = htmlParser.parse(template.source, '', true); const htmlResult = htmlParser.parse(template.source, '', true);
const analyzedModules = this.host.getAnalyzedModules(); const analyzedModules = this.host.getAnalyzedModules();
let errors: Diagnostic[] = undefined; let errors: Diagnostic[] = undefined !;
let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type); let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type);
if (!ngModule) { if (!ngModule) {
// Reported by the the declaration diagnostics. // Reported by the the declaration diagnostics.
@ -122,7 +122,7 @@ class LanguageServiceImpl implements LanguageService {
const resolvedDirectives = ngModule.transitiveModule.directives.map( const resolvedDirectives = ngModule.transitiveModule.directives.map(
d => this.host.resolver.getNonNormalizedDirectiveMetadata(d.reference)); d => this.host.resolver.getNonNormalizedDirectiveMetadata(d.reference));
const directives = const directives =
resolvedDirectives.filter(d => d !== null).map(d => d.metadata.toSummary()); resolvedDirectives.filter(d => d !== null).map(d => d !.metadata.toSummary());
const pipes = ngModule.transitiveModule.pipes.map( const pipes = ngModule.transitiveModule.pipes.map(
p => this.host.resolver.getOrLoadPipeMetadata(p.reference).toSummary()); p => this.host.resolver.getOrLoadPipeMetadata(p.reference).toSummary());
const schemas = ngModule.schemas; const schemas = ngModule.schemas;
@ -171,7 +171,7 @@ function uniqueBySpan < T extends {
} }
function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata { function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata {
let result: CompileNgModuleMetadata; let result: CompileNgModuleMetadata = undefined !;
let resultSize = 0; let resultSize = 0;
for (const module of modules.ngModules) { for (const module of modules.ngModules) {
const moduleSize = module.transitiveModule.directives.length; const moduleSize = module.transitiveModule.directives.length;

View File

@ -20,18 +20,18 @@ export interface SymbolInfo {
span: Span; span: Span;
} }
export function locateSymbol(info: TemplateInfo): SymbolInfo { export function locateSymbol(info: TemplateInfo): SymbolInfo|undefined {
const templatePosition = info.position - info.template.span.start; const templatePosition = info.position ! - info.template.span.start;
const path = new TemplateAstPath(info.templateAst, templatePosition); const path = new TemplateAstPath(info.templateAst, templatePosition);
if (path.tail) { if (path.tail) {
let symbol: Symbol = undefined; let symbol: Symbol = undefined !;
let span: Span = undefined; let span: Span = undefined !;
const attributeValueSymbol = (ast: AST, inEvent: boolean = false): boolean => { const attributeValueSymbol = (ast: AST, inEvent: boolean = false): boolean => {
const attribute = findAttribute(info); const attribute = findAttribute(info);
if (attribute) { if (attribute) {
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) { if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
const scope = getExpressionScope(info, path, inEvent); const scope = getExpressionScope(info, path, inEvent);
const expressionOffset = attribute.valueSpan.start.offset + 1; const expressionOffset = attribute.valueSpan !.start.offset + 1;
const result = getExpressionSymbol( const result = getExpressionSymbol(
scope, ast, templatePosition - expressionOffset, info.template.query); scope, ast, templatePosition - expressionOffset, info.template.query);
if (result) { if (result) {
@ -52,28 +52,28 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo {
if (component) { if (component) {
symbol = info.template.query.getTypeSymbol(component.directive.type.reference); symbol = info.template.query.getTypeSymbol(component.directive.type.reference);
symbol = symbol && new OverrideKindSymbol(symbol, 'component'); symbol = symbol && new OverrideKindSymbol(symbol, 'component');
span = spanOf(ast); span = spanOf(ast) !;
} else { } else {
// Find a directive that matches the element name // Find a directive that matches the element name
const directive = const directive =
ast.directives.find(d => d.directive.selector.indexOf(ast.name) >= 0); ast.directives.find(d => d.directive.selector !.indexOf(ast.name) >= 0);
if (directive) { if (directive) {
symbol = info.template.query.getTypeSymbol(directive.directive.type.reference); symbol = info.template.query.getTypeSymbol(directive.directive.type.reference);
symbol = symbol && new OverrideKindSymbol(symbol, 'directive'); symbol = symbol && new OverrideKindSymbol(symbol, 'directive');
span = spanOf(ast); span = spanOf(ast) !;
} }
} }
}, },
visitReference(ast) { visitReference(ast) {
symbol = info.template.query.getTypeSymbol(tokenReference(ast.value)); symbol = info.template.query.getTypeSymbol(tokenReference(ast.value));
span = spanOf(ast); span = spanOf(ast) !;
}, },
visitVariable(ast) {}, visitVariable(ast) {},
visitEvent(ast) { visitEvent(ast) {
if (!attributeValueSymbol(ast.handler, /* inEvent */ true)) { if (!attributeValueSymbol(ast.handler, /* inEvent */ true)) {
symbol = findOutputBinding(info, path, ast); symbol = findOutputBinding(info, path, ast) !;
symbol = symbol && new OverrideKindSymbol(symbol, 'event'); symbol = symbol && new OverrideKindSymbol(symbol, 'event');
span = spanOf(ast); span = spanOf(ast) !;
} }
}, },
visitElementProperty(ast) { attributeValueSymbol(ast.value); }, visitElementProperty(ast) { attributeValueSymbol(ast.value); },
@ -93,12 +93,12 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo {
visitText(ast) {}, visitText(ast) {},
visitDirective(ast) { visitDirective(ast) {
symbol = info.template.query.getTypeSymbol(ast.directive.type.reference); symbol = info.template.query.getTypeSymbol(ast.directive.type.reference);
span = spanOf(ast); span = spanOf(ast) !;
}, },
visitDirectiveProperty(ast) { visitDirectiveProperty(ast) {
if (!attributeValueSymbol(ast.value)) { if (!attributeValueSymbol(ast.value)) {
symbol = findInputBinding(info, path, ast); symbol = findInputBinding(info, path, ast) !;
span = spanOf(ast); span = spanOf(ast) !;
} }
} }
}, },
@ -109,14 +109,15 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo {
} }
} }
function findAttribute(info: TemplateInfo): Attribute { function findAttribute(info: TemplateInfo): Attribute|undefined {
const templatePosition = info.position - info.template.span.start; const templatePosition = info.position ! - info.template.span.start;
const path = new HtmlAstPath(info.htmlAst, templatePosition); const path = new HtmlAstPath(info.htmlAst, templatePosition);
return path.first(Attribute); return path.first(Attribute);
} }
function findInputBinding( function findInputBinding(
info: TemplateInfo, path: TemplateAstPath, binding: BoundDirectivePropertyAst): Symbol { info: TemplateInfo, path: TemplateAstPath, binding: BoundDirectivePropertyAst): Symbol|
undefined {
const element = path.first(ElementAst); const element = path.first(ElementAst);
if (element) { if (element) {
for (const directive of element.directives) { for (const directive of element.directives) {
@ -133,7 +134,7 @@ function findInputBinding(
} }
function findOutputBinding( function findOutputBinding(
info: TemplateInfo, path: TemplateAstPath, binding: BoundEventAst): Symbol { info: TemplateInfo, path: TemplateAstPath, binding: BoundEventAst): Symbol|undefined {
const element = path.first(ElementAst); const element = path.first(ElementAst);
if (element) { if (element) {
for (const directive of element.directives) { for (const directive of element.directives) {

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost { class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
constructor(private host: ts.LanguageServiceHost) { constructor(private host: ts.LanguageServiceHost) {
if (host.directoryExists) if (host.directoryExists)
this.directoryExists = directoryName => this.host.directoryExists(directoryName); this.directoryExists = directoryName => this.host.directoryExists !(directoryName);
} }
fileExists(fileName: string): boolean { return !!this.host.getScriptSnapshot(fileName); } fileExists(fileName: string): boolean { return !!this.host.getScriptSnapshot(fileName); }
@ -22,6 +22,7 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
if (snapshot) { if (snapshot) {
return snapshot.getText(0, snapshot.getLength()); return snapshot.getText(0, snapshot.getLength());
} }
return undefined !;
} }
directoryExists: (directoryName: string) => boolean; directoryExists: (directoryName: string) => boolean;
@ -32,7 +33,7 @@ export class ReflectorHost extends CompilerHost {
private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost, private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
options: AngularCompilerOptions) { options: AngularCompilerOptions) {
super( super(
null, options, null !, options,
new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)), new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)),
{verboseInvalidExpression: true}); {verboseInvalidExpression: true});
} }

View File

@ -104,10 +104,10 @@ class TemplateAstPathBuilder extends TemplateAstChildVisitor {
constructor(private position: number, private allowWidening: boolean) { super(); } constructor(private position: number, private allowWidening: boolean) { super(); }
visit(ast: TemplateAst, context: any): any { visit(ast: TemplateAst, context: any): any {
let span = spanOf(ast); let span = spanOf(ast) !;
if (inSpan(this.position, span)) { if (inSpan(this.position, span)) {
const len = this.path.length; const len = this.path.length;
if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]))) { if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]) !)) {
this.path.push(ast); this.path.push(ast);
} }
} else { } else {

View File

@ -45,7 +45,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
} }
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, info.languageService); const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, info.languageService);
const ls = createLanguageService(serviceHost); const ls = createLanguageService(serviceHost as any);
serviceHost.setSite(ls); serviceHost.setSite(ls);
proxy.getCompletionsAtPosition = function(fileName: string, position: number) { proxy.getCompletionsAtPosition = function(fileName: string, position: number) {
@ -76,7 +76,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
if (ours) { if (ours) {
const displayParts: typeof base.displayParts = []; const displayParts: typeof base.displayParts = [];
for (const part of ours.text) { for (const part of ours.text) {
displayParts.push({kind: part.language, text: part.text}); displayParts.push({kind: part.language !, text: part.text});
} }
base = { base = {
displayParts, displayParts,

View File

@ -84,7 +84,7 @@ export interface TemplateSource {
* *
* @experimental * @experimental
*/ */
export type TemplateSources = TemplateSource[] /* | undefined */; export type TemplateSources = TemplateSource[] | undefined;
/** /**
* Error information found getting declaration information * Error information found getting declaration information
@ -227,7 +227,7 @@ export interface Symbol {
/** /**
* A symbol representing type of the symbol. * A symbol representing type of the symbol.
*/ */
readonly type: Symbol /* | undefined */; readonly type: Symbol|undefined;
/** /**
@ -235,7 +235,7 @@ export interface Symbol {
* is the class or interface of the method. If no container is appropriate, undefined is * is the class or interface of the method. If no container is appropriate, undefined is
* returned. * returned.
*/ */
readonly container: Symbol /* | undefined */; readonly container: Symbol|undefined;
/** /**
* The symbol is public in the container. * The symbol is public in the container.
@ -250,7 +250,7 @@ export interface Symbol {
/** /**
* The location of the definition of the symbol * The location of the definition of the symbol
*/ */
readonly definition: Definition; readonly definition: Definition|undefined;
/** /**
* A table of the members of the symbol; that is, the members that can appear * A table of the members of the symbol; that is, the members that can appear
@ -270,13 +270,13 @@ export interface Symbol {
* given the `types` supplied. If no signature would match, this method should * given the `types` supplied. If no signature would match, this method should
* return `undefined`. * return `undefined`.
*/ */
selectSignature(types: Symbol[]): Signature /* | undefined */; selectSignature(types: Symbol[]): Signature|undefined;
/** /**
* Return the type of the expression if this symbol is indexed by `argument`. * Return the type of the expression if this symbol is indexed by `argument`.
* If the symbol cannot be indexed, this method should return `undefined`. * If the symbol cannot be indexed, this method should return `undefined`.
*/ */
indexed(argument: Symbol): Symbol /* | undefined */; indexed(argument: Symbol): Symbol|undefined;
} }
/** /**
@ -296,7 +296,7 @@ export interface SymbolTable {
* Get the symbol corresponding to `key` or `undefined` if there is no symbol in the * Get the symbol corresponding to `key` or `undefined` if there is no symbol in the
* table by the name `key`. * table by the name `key`.
*/ */
get(key: string): Symbol /* | undefined */; get(key: string): Symbol|undefined;
/** /**
* Returns `true` if the table contains a `Symbol` with the name `key`. * Returns `true` if the table contains a `Symbol` with the name `key`.
@ -364,7 +364,7 @@ export interface SymbolQuery {
* Return element type symbol for an array type if the `type` is an array type. Otherwise return * Return element type symbol for an array type if the `type` is an array type. Otherwise return
* undefined. * undefined.
*/ */
getElementType(type: Symbol): Symbol /* | undefined */; getElementType(type: Symbol): Symbol|undefined;
/** /**
* Return a type that is the non-nullable version of the given type. If `type` is already * Return a type that is the non-nullable version of the given type. If `type` is already
@ -385,7 +385,7 @@ export interface SymbolQuery {
/** /**
* Return the members that are in the context of a type's template reference. * Return the members that are in the context of a type's template reference.
*/ */
getTemplateContext(type: StaticSymbol): SymbolTable; getTemplateContext(type: StaticSymbol): SymbolTable|undefined;
/** /**
* Produce a symbol table with the given symbols. Used to produce a symbol table * Produce a symbol table with the given symbols. Used to produce a symbol table
@ -406,7 +406,7 @@ export interface SymbolQuery {
/** /**
* Return the span of the narrowest non-token node at the given location. * Return the span of the narrowest non-token node at the given location.
*/ */
getSpanAt(line: number, column: number): Span /* | undefined */; getSpanAt(line: number, column: number): Span|undefined;
} }
/** /**
@ -519,7 +519,7 @@ export interface Completion {
* *
* @experimental * @experimental
*/ */
export type Completions = Completion[] /* | undefined */; export type Completions = Completion[] | undefined;
/** /**
* A file and span. * A file and span.
@ -532,7 +532,7 @@ export interface Location {
/** /**
* A defnition location(s). * A defnition location(s).
*/ */
export type Definition = Location[] /* | undefined */; export type Definition = Location[] | undefined;
/** /**
* The kind of diagnostic message. * The kind of diagnostic message.
@ -597,7 +597,7 @@ export interface PipeInfo {
* *
* @experimental * @experimental
*/ */
export type Pipes = PipeInfo[] /* | undefined */; export type Pipes = PipeInfo[] | undefined;
/** /**
* Describes a symbol to type binding used to build a symbol table. * Describes a symbol to type binding used to build a symbol table.
@ -699,30 +699,30 @@ export interface LanguageService {
/** /**
* Returns a list of all the external templates referenced by the project. * Returns a list of all the external templates referenced by the project.
*/ */
getTemplateReferences(): string[] /* | undefined */; getTemplateReferences(): string[]|undefined;
/** /**
* Returns a list of all error for all templates in the given file. * Returns a list of all error for all templates in the given file.
*/ */
getDiagnostics(fileName: string): Diagnostics /* | undefined */; getDiagnostics(fileName: string): Diagnostics|undefined;
/** /**
* Return the completions at the given position. * Return the completions at the given position.
*/ */
getCompletionsAt(fileName: string, position: number): Completions /* | undefined */; getCompletionsAt(fileName: string, position: number): Completions|undefined;
/** /**
* Return the definition location for the symbol at position. * Return the definition location for the symbol at position.
*/ */
getDefinitionAt(fileName: string, position: number): Definition /* | undefined */; getDefinitionAt(fileName: string, position: number): Definition|undefined;
/** /**
* Return the hover information for the symbol at position. * Return the hover information for the symbol at position.
*/ */
getHoverAt(fileName: string, position: number): Hover /* | undefined */; getHoverAt(fileName: string, position: number): Hover|undefined;
/** /**
* Return the pipes that are available at the given position. * Return the pipes that are available at the given position.
*/ */
getPipesAt(fileName: string, position: number): Pipes /* | undefined */; getPipesAt(fileName: string, position: number): Pipes|undefined;
} }

View File

@ -128,14 +128,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return this.templateReferences; return this.templateReferences;
} }
getTemplateAt(fileName: string, position: number): TemplateSource|undefined { getTemplateAt(fileName: string, position: number): TemplateSource {
let sourceFile = this.getSourceFile(fileName); let sourceFile = this.getSourceFile(fileName);
if (sourceFile) { if (sourceFile) {
this.context = sourceFile.fileName; this.context = sourceFile.fileName;
let node = this.findNode(sourceFile, position); let node = this.findNode(sourceFile, position);
if (node) { if (node) {
return this.getSourceFromNode( return this.getSourceFromNode(
fileName, this.host.getScriptVersion(sourceFile.fileName), node); fileName, this.host.getScriptVersion(sourceFile.fileName), node) !;
} }
} else { } else {
this.ensureTemplateMap(); this.ensureTemplateMap();
@ -143,9 +143,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const componentType = this.fileToComponent.get(fileName); const componentType = this.fileToComponent.get(fileName);
if (componentType) { if (componentType) {
return this.getSourceFromType( return this.getSourceFromType(
fileName, this.host.getScriptVersion(fileName), componentType); fileName, this.host.getScriptVersion(fileName), componentType) !;
} }
} }
return null !;
} }
getAnalyzedModules(): NgAnalyzedModules { getAnalyzedModules(): NgAnalyzedModules {
@ -222,10 +223,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
updateAnalyzedModules() { updateAnalyzedModules() {
this.validate(); this.validate();
if (this.modulesOutOfDate) { if (this.modulesOutOfDate) {
this.analyzedModules = null; this.analyzedModules = null !;
this._reflector = null; this._reflector = null !;
this.templateReferences = null; this.templateReferences = null !;
this.fileToComponent = null; this.fileToComponent = null !;
this.ensureAnalyzedModules(); this.ensureAnalyzedModules();
this.modulesOutOfDate = false; this.modulesOutOfDate = false;
} }
@ -270,10 +271,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
} }
private clearCaches() { private clearCaches() {
this._checker = null; this._checker = null !;
this._typeCache = []; this._typeCache = [];
this._resolver = null; this._resolver = null !;
this.collectedErrors = null; this.collectedErrors = null !;
this.modulesOutOfDate = true; this.modulesOutOfDate = true;
} }
@ -286,7 +287,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
for (const module of ngModuleSummary.ngModules) { for (const module of ngModuleSummary.ngModules) {
for (const directive of module.declaredDirectives) { for (const directive of module.declaredDirectives) {
const {metadata, annotation} = const {metadata, annotation} =
this.resolver.getNonNormalizedDirectiveMetadata(directive.reference); this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !;
if (metadata.isComponent && metadata.template && metadata.template.templateUrl) { if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
const templateName = urlResolver.resolve( const templateName = urlResolver.resolve(
componentModuleUrl(this.reflector, directive.reference, annotation), componentModuleUrl(this.reflector, directive.reference, annotation),
@ -343,7 +344,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (declaration && declaration.name) { if (declaration && declaration.name) {
const sourceFile = this.getSourceFile(fileName); const sourceFile = this.getSourceFile(fileName);
return this.getSourceFromDeclaration( return this.getSourceFromDeclaration(
fileName, version, this.stringOf(node), shrink(spanOf(node)), fileName, version, this.stringOf(node) !, shrink(spanOf(node)),
this.reflector.getStaticSymbol(sourceFile.fileName, declaration.name.text), this.reflector.getStaticSymbol(sourceFile.fileName, declaration.name.text),
declaration, node, sourceFile); declaration, node, sourceFile);
} }
@ -357,7 +358,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
let result: TemplateSource|undefined = undefined; let result: TemplateSource|undefined = undefined;
const declaration = this.getTemplateClassFromStaticSymbol(type); const declaration = this.getTemplateClassFromStaticSymbol(type);
if (declaration) { if (declaration) {
const snapshot = this.host.getScriptSnapshot(fileName); const snapshot = this.host.getScriptSnapshot(fileName) !;
const source = snapshot.getText(0, snapshot.getLength()); const source = snapshot.getText(0, snapshot.getLength());
result = this.getSourceFromDeclaration( result = this.getSourceFromDeclaration(
fileName, version, source, {start: 0, end: source.length}, type, declaration, declaration, fileName, version, source, {start: 0, end: source.length}, type, declaration, declaration,
@ -410,14 +411,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (!result) { if (!result) {
this._summaryResolver = new AotSummaryResolver( this._summaryResolver = new AotSummaryResolver(
{ {
loadSummary(filePath: string) { return null; }, loadSummary(filePath: string) { return null !; },
isSourceFile(sourceFilePath: string) { return true; }, isSourceFile(sourceFilePath: string) { return true !; },
getOutputFileName(sourceFilePath: string) { return null; } getOutputFileName(sourceFilePath: string) { return null !; }
}, },
this._staticSymbolCache); this._staticSymbolCache);
result = this._staticSymbolResolver = new StaticSymbolResolver( result = this._staticSymbolResolver = new StaticSymbolResolver(
this.reflectorHost, this._staticSymbolCache, this._summaryResolver, this.reflectorHost as any, this._staticSymbolCache, this._summaryResolver,
(e, filePath) => this.collectError(e, filePath)); (e, filePath) => this.collectError(e, filePath !));
} }
return result; return result;
} }
@ -427,7 +428,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (!result) { if (!result) {
const ssr = this.staticSymbolResolver; const ssr = this.staticSymbolResolver;
result = this._reflector = new StaticReflector( result = this._reflector = new StaticReflector(
this._summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath)); this._summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !));
StaticAndDynamicReflectionCapabilities.install(result); StaticAndDynamicReflectionCapabilities.install(result);
} }
return result; return result;
@ -439,7 +440,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const declarationNode = ts.forEachChild(source, child => { const declarationNode = ts.forEachChild(source, child => {
if (child.kind === ts.SyntaxKind.ClassDeclaration) { if (child.kind === ts.SyntaxKind.ClassDeclaration) {
const classDeclaration = child as ts.ClassDeclaration; const classDeclaration = child as ts.ClassDeclaration;
if (classDeclaration.name.text === type.name) { if (classDeclaration.name !.text === type.name) {
return classDeclaration; return classDeclaration;
} }
} }
@ -450,14 +451,15 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return undefined; return undefined;
} }
private static missingTemplate = <[ts.ClassDeclaration, ts.Expression]>[]; private static missingTemplate: [ts.ClassDeclaration | undefined, ts.Expression|undefined] =
[undefined, undefined];
/** /**
* Given a template string node, see if it is an Angular template string, and if so return the * Given a template string node, see if it is an Angular template string, and if so return the
* containing class. * containing class.
*/ */
private getTemplateClassDeclFromNode(currentToken: ts.Node): private getTemplateClassDeclFromNode(currentToken: ts.Node):
[ts.ClassDeclaration, ts.Expression] { [ts.ClassDeclaration | undefined, ts.Expression|undefined] {
// Verify we are in a 'template' property assignment, in an object literal, which is an call // Verify we are in a 'template' property assignment, in an object literal, which is an call
// arg, in a decorator // arg, in a decorator
let parentNode = currentToken.parent; // PropertyAssignment let parentNode = currentToken.parent; // PropertyAssignment
@ -519,7 +521,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
try { try {
if (this.resolver.isDirective(staticSymbol as any)) { if (this.resolver.isDirective(staticSymbol as any)) {
const {metadata} = const {metadata} =
this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any); this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any) !;
const declarationSpan = spanOf(target); const declarationSpan = spanOf(target);
return { return {
type: staticSymbol, type: staticSymbol,
@ -609,7 +611,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile, private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile,
private fetchPipes: () => SymbolTable) {} private fetchPipes: () => SymbolTable) {}
getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol)); } getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol) !); }
getBuiltinType(kind: BuiltinType): Symbol { getBuiltinType(kind: BuiltinType): Symbol {
// TODO: Replace with typeChecker API when available. // TODO: Replace with typeChecker API when available.
@ -637,7 +639,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return this.getBuiltinType(BuiltinType.Any); return this.getBuiltinType(BuiltinType.Any);
} }
getElementType(type: Symbol): Symbol { getElementType(type: Symbol): Symbol|undefined {
if (type instanceof TypeWrapper) { if (type instanceof TypeWrapper) {
const elementType = getTypeParameterOf(type.tsType, 'Array'); const elementType = getTypeParameterOf(type.tsType, 'Array');
if (elementType) { if (elementType) {
@ -659,7 +661,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return result; return result;
} }
getTemplateContext(type: StaticSymbol): SymbolTable { getTemplateContext(type: StaticSymbol): SymbolTable|undefined {
const context: TypeContext = {node: this.source, program: this.program, checker: this.checker}; const context: TypeContext = {node: this.source, program: this.program, checker: this.checker};
const typeSymbol = findClassSymbolInContext(type, context); const typeSymbol = findClassSymbolInContext(type, context);
if (typeSymbol) { if (typeSymbol) {
@ -670,7 +672,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
getTypeSymbol(type: StaticSymbol): Symbol { getTypeSymbol(type: StaticSymbol): Symbol {
const context: TypeContext = {node: this.source, program: this.program, checker: this.checker}; const context: TypeContext = {node: this.source, program: this.program, checker: this.checker};
const typeSymbol = findClassSymbolInContext(type, context); const typeSymbol = findClassSymbolInContext(type, context) !;
return new SymbolWrapper(typeSymbol, context); return new SymbolWrapper(typeSymbol, context);
} }
@ -688,15 +690,17 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return result; return result;
} }
getSpanAt(line: number, column: number): Span { return spanAt(this.source, line, column); } getSpanAt(line: number, column: number): Span|undefined {
return spanAt(this.source, line, column);
}
private getTemplateRefContextType(type: ts.Symbol): ts.Symbol { private getTemplateRefContextType(type: ts.Symbol): ts.Symbol|undefined {
const constructor = type.members && type.members['__constructor']; const constructor = type.members && type.members !['__constructor'];
if (constructor) { if (constructor) {
const constructorDeclaration = constructor.declarations[0] as ts.ConstructorTypeNode; const constructorDeclaration = constructor.declarations ![0] as ts.ConstructorTypeNode;
for (const parameter of constructorDeclaration.parameters) { for (const parameter of constructorDeclaration.parameters) {
const type = this.checker.getTypeAtLocation(parameter.type); const type = this.checker.getTypeAtLocation(parameter.type !);
if (type.symbol.name == 'TemplateRef' && isReferenceType(type)) { if (type.symbol !.name == 'TemplateRef' && isReferenceType(type)) {
const typeReference = type as ts.TypeReference; const typeReference = type as ts.TypeReference;
if (typeReference.typeArguments.length === 1) { if (typeReference.typeArguments.length === 1) {
return typeReference.typeArguments[0].symbol; return typeReference.typeArguments[0].symbol;
@ -706,7 +710,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
} }
} }
private getTsTypeOf(symbol: Symbol): ts.Type { private getTsTypeOf(symbol: Symbol): ts.Type|undefined {
const type = this.getTypeWrapper(symbol); const type = this.getTypeWrapper(symbol);
return type && type.tsType; return type && type.tsType;
} }
@ -838,7 +842,7 @@ class SymbolWrapper implements Symbol {
const typeWrapper = new TypeWrapper(declaredType, this.context); const typeWrapper = new TypeWrapper(declaredType, this.context);
this._members = typeWrapper.members(); this._members = typeWrapper.members();
} else { } else {
this._members = new SymbolTableWrapper(this.symbol.members, this.context); this._members = new SymbolTableWrapper(this.symbol.members !, this.context);
} }
} }
return this._members; return this._members;
@ -949,7 +953,7 @@ class MapSymbolTable implements SymbolTable {
add(symbol: Symbol) { add(symbol: Symbol) {
if (this.map.has(symbol.name)) { if (this.map.has(symbol.name)) {
const previous = this.map.get(symbol.name); const previous = this.map.get(symbol.name) !;
this._values[this._values.indexOf(previous)] = symbol; this._values[this._values.indexOf(previous)] = symbol;
} }
this.map.set(symbol.name, symbol); this.map.set(symbol.name, symbol);
@ -973,18 +977,18 @@ class MapSymbolTable implements SymbolTable {
class PipesTable implements SymbolTable { class PipesTable implements SymbolTable {
constructor(private pipes: Pipes, private context: TypeContext) {} constructor(private pipes: Pipes, private context: TypeContext) {}
get size() { return this.pipes.length; } get size() { return this.pipes !.length; }
get(key: string): Symbol { get(key: string): Symbol|undefined {
const pipe = this.pipes.find(pipe => pipe.name == key); const pipe = this.pipes !.find(pipe => pipe.name == key);
if (pipe) { if (pipe) {
return new PipeSymbol(pipe, this.context); return new PipeSymbol(pipe, this.context);
} }
} }
has(key: string): boolean { return this.pipes.find(pipe => pipe.name == key) != null; } has(key: string): boolean { return this.pipes !.find(pipe => pipe.name == key) != null; }
values(): Symbol[] { return this.pipes.map(pipe => new PipeSymbol(pipe, this.context)); } values(): Symbol[] { return this.pipes !.map(pipe => new PipeSymbol(pipe, this.context)); }
} }
class PipeSymbol implements Symbol { class PipeSymbol implements Symbol {
@ -1013,11 +1017,11 @@ class PipeSymbol implements Symbol {
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
selectSignature(types: Symbol[]): Signature|undefined { selectSignature(types: Symbol[]): Signature|undefined {
let signature = selectSignature(this.tsType, this.context, types); let signature = selectSignature(this.tsType, this.context, types) !;
if (types.length == 1) { if (types.length == 1) {
const parameterType = types[0]; const parameterType = types[0];
if (parameterType instanceof TypeWrapper) { if (parameterType instanceof TypeWrapper) {
let resultType: ts.Type = undefined; let resultType: ts.Type|undefined = undefined;
switch (this.name) { switch (this.name) {
case 'async': case 'async':
switch (parameterType.name) { switch (parameterType.name) {
@ -1051,7 +1055,7 @@ class PipeSymbol implements Symbol {
if (!type) { if (!type) {
const classSymbol = this.findClassSymbol(this.pipe.symbol); const classSymbol = this.findClassSymbol(this.pipe.symbol);
if (classSymbol) { if (classSymbol) {
type = this._tsType = this.findTransformMethodType(classSymbol); type = this._tsType = this.findTransformMethodType(classSymbol) !;
} }
if (!type) { if (!type) {
type = this._tsType = getBuiltinTypeFromTs(BuiltinType.Any, this.context); type = this._tsType = getBuiltinTypeFromTs(BuiltinType.Any, this.context);
@ -1060,7 +1064,7 @@ class PipeSymbol implements Symbol {
return type; return type;
} }
private findClassSymbol(type: StaticSymbol): ts.Symbol { private findClassSymbol(type: StaticSymbol): ts.Symbol|undefined {
return findClassSymbolInContext(type, this.context); return findClassSymbolInContext(type, this.context);
} }
@ -1075,7 +1079,7 @@ class PipeSymbol implements Symbol {
} }
} }
function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.Symbol { function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.Symbol|undefined {
const sourceFile = context.program.getSourceFile(type.filePath); const sourceFile = context.program.getSourceFile(type.filePath);
if (sourceFile) { if (sourceFile) {
const moduleSymbol = (sourceFile as any).module || (sourceFile as any).symbol; const moduleSymbol = (sourceFile as any).module || (sourceFile as any).symbol;
@ -1092,7 +1096,7 @@ class EmptyTable implements SymbolTable {
static instance = new EmptyTable(); static instance = new EmptyTable();
} }
function findTsConfig(fileName: string): string { function findTsConfig(fileName: string): string|undefined {
let dir = path.dirname(fileName); let dir = path.dirname(fileName);
while (fs.existsSync(dir)) { while (fs.existsSync(dir)) {
const candidate = path.join(dir, 'tsconfig.json'); const candidate = path.join(dir, 'tsconfig.json');
@ -1110,7 +1114,7 @@ function isBindingPattern(node: ts.Node): node is ts.BindingPattern {
function walkUpBindingElementsAndPatterns(node: ts.Node): ts.Node { function walkUpBindingElementsAndPatterns(node: ts.Node): ts.Node {
while (node && (node.kind === ts.SyntaxKind.BindingElement || isBindingPattern(node))) { while (node && (node.kind === ts.SyntaxKind.BindingElement || isBindingPattern(node))) {
node = node.parent; node = node.parent !;
} }
return node; return node;
@ -1121,12 +1125,12 @@ function getCombinedNodeFlags(node: ts.Node): ts.NodeFlags {
let flags = node.flags; let flags = node.flags;
if (node.kind === ts.SyntaxKind.VariableDeclaration) { if (node.kind === ts.SyntaxKind.VariableDeclaration) {
node = node.parent; node = node.parent !;
} }
if (node && node.kind === ts.SyntaxKind.VariableDeclarationList) { if (node && node.kind === ts.SyntaxKind.VariableDeclarationList) {
flags |= node.flags; flags |= node.flags;
node = node.parent; node = node.parent !;
} }
if (node && node.kind === ts.SyntaxKind.VariableStatement) { if (node && node.kind === ts.SyntaxKind.VariableStatement) {
@ -1137,7 +1141,7 @@ function getCombinedNodeFlags(node: ts.Node): ts.NodeFlags {
} }
function isSymbolPrivate(s: ts.Symbol): boolean { function isSymbolPrivate(s: ts.Symbol): boolean {
return s.valueDeclaration && isPrivate(s.valueDeclaration); return !!s.valueDeclaration && isPrivate(s.valueDeclaration);
} }
function getBuiltinTypeFromTs(kind: BuiltinType, context: TypeContext): ts.Type { function getBuiltinTypeFromTs(kind: BuiltinType, context: TypeContext): ts.Type {
@ -1200,10 +1204,10 @@ function shrink(span: Span, offset?: number) {
return {start: span.start + offset, end: span.end - offset}; return {start: span.start + offset, end: span.end - offset};
} }
function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span { function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span|undefined {
if (line != null && column != null) { if (line != null && column != null) {
const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column); const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
const findChild = function findChild(node: ts.Node): ts.Node { const findChild = function findChild(node: ts.Node): ts.Node | undefined {
if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) { if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
const betterNode = ts.forEachChild(node, findChild); const betterNode = ts.forEachChild(node, findChild);
return betterNode || node; return betterNode || node;
@ -1230,20 +1234,20 @@ function definitionFromTsSymbol(symbol: ts.Symbol): Definition {
} }
} }
function parentDeclarationOf(node: ts.Node): ts.Node { function parentDeclarationOf(node: ts.Node): ts.Node|undefined {
while (node) { while (node) {
switch (node.kind) { switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.InterfaceDeclaration: case ts.SyntaxKind.InterfaceDeclaration:
return node; return node;
case ts.SyntaxKind.SourceFile: case ts.SyntaxKind.SourceFile:
return null; return undefined;
} }
node = node.parent; node = node.parent !;
} }
} }
function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol { function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol|undefined {
if (symbol.getFlags() & ts.SymbolFlags.ClassMember && symbol.declarations) { if (symbol.getFlags() & ts.SymbolFlags.ClassMember && symbol.declarations) {
for (const declaration of symbol.declarations) { for (const declaration of symbol.declarations) {
const parent = parentDeclarationOf(declaration); const parent = parentDeclarationOf(declaration);
@ -1257,7 +1261,7 @@ function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol {
} }
} }
function getTypeParameterOf(type: ts.Type, name: string): ts.Type { function getTypeParameterOf(type: ts.Type, name: string): ts.Type|undefined {
if (type && type.symbol && type.symbol.name == name) { if (type && type.symbol && type.symbol.name == name) {
const typeArguments: ts.Type[] = (type as any).typeArguments; const typeArguments: ts.Type[] = (type as any).typeArguments;
if (typeArguments && typeArguments.length <= 1) { if (typeArguments && typeArguments.length <= 1) {
@ -1281,10 +1285,10 @@ function typeKindOf(type: ts.Type): BuiltinType {
return BuiltinType.Null; return BuiltinType.Null;
} else if (type.flags & ts.TypeFlags.Union) { } else if (type.flags & ts.TypeFlags.Union) {
// If all the constituent types of a union are the same kind, it is also that kind. // If all the constituent types of a union are the same kind, it is also that kind.
let candidate: BuiltinType; let candidate: BuiltinType = undefined !;
const unionType = type as ts.UnionType; const unionType = type as ts.UnionType;
if (unionType.types.length > 0) { if (unionType.types.length > 0) {
candidate = typeKindOf(unionType.types[0]); candidate = typeKindOf(unionType.types[0]) !;
for (const subType of unionType.types) { for (const subType of unionType.types) {
if (candidate != typeKindOf(subType)) { if (candidate != typeKindOf(subType)) {
return BuiltinType.Other; return BuiltinType.Other;

View File

@ -13,7 +13,7 @@ import {Span} from './types';
export interface SpanHolder { export interface SpanHolder {
sourceSpan: ParseSourceSpan; sourceSpan: ParseSourceSpan;
endSourceSpan?: ParseSourceSpan; endSourceSpan?: ParseSourceSpan|null;
children?: SpanHolder[]; children?: SpanHolder[];
} }
@ -21,7 +21,7 @@ export function isParseSourceSpan(value: any): value is ParseSourceSpan {
return value && !!value.start; return value && !!value.start;
} }
export function spanOf(span?: SpanHolder | ParseSourceSpan): Span { export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
if (!span) return undefined; if (!span) return undefined;
if (isParseSourceSpan(span)) { if (isParseSourceSpan(span)) {
return {start: span.start.offset, end: span.end.offset}; return {start: span.start.offset, end: span.end.offset};
@ -31,7 +31,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span {
} else if (span.children && span.children.length) { } else if (span.children && span.children.length) {
return { return {
start: span.sourceSpan.start.offset, start: span.sourceSpan.start.offset,
end: spanOf(span.children[span.children.length - 1]).end end: spanOf(span.children[span.children.length - 1]) !.end
}; };
} }
return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset}; return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset};
@ -40,7 +40,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span {
export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean { export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean {
return span && exclusive ? position >= span.start && position < span.end : return span && exclusive ? position >= span.start && position < span.end :
position >= span.start && position <= span.end; position >= span !.start && position <= span !.end;
} }
export function offsetSpan(span: Span, amount: number): Span { export function offsetSpan(span: Span, amount: number): Span {
@ -54,7 +54,7 @@ export function isNarrower(spanA: Span, spanB: Span): boolean {
export function hasTemplateReference(type: CompileTypeMetadata): boolean { export function hasTemplateReference(type: CompileTypeMetadata): boolean {
if (type.diDeps) { if (type.diDeps) {
for (let diDep of type.diDeps) { for (let diDep of type.diDeps) {
if (diDep.token.identifier && identifierName(diDep.token.identifier) == 'TemplateRef') if (diDep.token !.identifier && identifierName(diDep.token !.identifier !) == 'TemplateRef')
return true; return true;
} }
} }
@ -63,8 +63,8 @@ export function hasTemplateReference(type: CompileTypeMetadata): boolean {
export function getSelectors(info: TemplateInfo): SelectorInfo { export function getSelectors(info: TemplateInfo): SelectorInfo {
const map = new Map<CssSelector, CompileDirectiveSummary>(); const map = new Map<CssSelector, CompileDirectiveSummary>();
const selectors = flatten(info.directives.map(directive => { const selectors: CssSelector[] = flatten(info.directives.map(directive => {
const selectors = CssSelector.parse(directive.selector); const selectors: CssSelector[] = CssSelector.parse(directive.selector !);
selectors.forEach(selector => map.set(selector, directive)); selectors.forEach(selector => map.set(selector, directive));
return selectors; return selectors;
})); }));

View File

@ -48,7 +48,7 @@ describe('completions', () => {
const fileName = '/app/test.ng'; const fileName = '/app/test.ng';
mockHost.override(fileName, ' > {{tle<\n {{retl ><bel/beled}}di>\n la</b </d &a '); mockHost.override(fileName, ' > {{tle<\n {{retl ><bel/beled}}di>\n la</b </d &a ');
expect(() => ngService.getCompletionsAt(fileName, 31)).not.toThrow(); expect(() => ngService.getCompletionsAt(fileName, 31)).not.toThrow();
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
}); });
it('should be able to infer the type of a ngForOf', () => { it('should be able to infer the type of a ngForOf', () => {
@ -101,7 +101,7 @@ describe('completions', () => {
} }
} }
try { try {
const originalContent = mockHost.getFileContent(fileName); const originalContent = mockHost.getFileContent(fileName) !;
// For each character in the file, add it to the file and request a completion after it. // For each character in the file, add it to the file and request a completion after it.
for (let index = 0, len = originalContent.length; index < len; index++) { for (let index = 0, len = originalContent.length; index < len; index++) {
@ -133,7 +133,7 @@ describe('completions', () => {
tryCompletionsAt(position); tryCompletionsAt(position);
}); });
} finally { } finally {
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
} }
}).not.toThrow(); }).not.toThrow();
}); });
@ -169,12 +169,12 @@ export class MyComponent {
try { try {
cb(fileName, newContent); cb(fileName, newContent);
} finally { } finally {
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
} }
} }
function contains(fileName: string, locationMarker: string, ...names: string[]) { function contains(fileName: string, locationMarker: string, ...names: string[]) {
let location = mockHost.getMarkerLocations(fileName)[locationMarker]; let location = mockHost.getMarkerLocations(fileName) ![locationMarker];
if (location == null) { if (location == null) {
throw new Error(`No marker ${locationMarker} found.`); throw new Error(`No marker ${locationMarker} found.`);
} }

View File

@ -70,7 +70,7 @@ describe('definitions', () => {
function localReference(code: string) { function localReference(code: string) {
addCode(code, fileName => { addCode(code, fileName => {
const refResult = mockHost.getReferenceMarkers(fileName); const refResult = mockHost.getReferenceMarkers(fileName) !;
for (const name in refResult.references) { for (const name in refResult.references) {
const references = refResult.references[name]; const references = refResult.references[name];
const definitions = refResult.definitions[name]; const definitions = refResult.definitions[name];
@ -100,14 +100,14 @@ describe('definitions', () => {
const definition: string = p2 ? p1 : undefined; const definition: string = p2 ? p1 : undefined;
let span: Span = p2 && p1.start != null ? p1 : undefined; let span: Span = p2 && p1.start != null ? p1 : undefined;
if (definition && !span) { if (definition && !span) {
const referencedFileMarkers = mockHost.getReferenceMarkers(referencedFile); const referencedFileMarkers = mockHost.getReferenceMarkers(referencedFile) !;
expect(referencedFileMarkers).toBeDefined(); // If this fails the test data is wrong. expect(referencedFileMarkers).toBeDefined(); // If this fails the test data is wrong.
const spans = referencedFileMarkers.definitions[definition]; const spans = referencedFileMarkers.definitions[definition];
expect(spans).toBeDefined(); // If this fails the test data is wrong. expect(spans).toBeDefined(); // If this fails the test data is wrong.
span = spans[0]; span = spans[0];
} }
addCode(code, fileName => { addCode(code, fileName => {
const refResult = mockHost.getReferenceMarkers(fileName); const refResult = mockHost.getReferenceMarkers(fileName) !;
let tests = 0; let tests = 0;
for (const name in refResult.references) { for (const name in refResult.references) {
const references = refResult.references[name]; const references = refResult.references[name];
@ -144,12 +144,12 @@ describe('definitions', () => {
try { try {
cb(fileName, newContent); cb(fileName, newContent);
} finally { } finally {
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
} }
} }
}); });
function matchingSpan(aSpans: Span[], bSpans: Span[]): Span { function matchingSpan(aSpans: Span[], bSpans: Span[]): Span|undefined {
for (const a of aSpans) { for (const a of aSpans) {
for (const b of bSpans) { for (const b of bSpans) {
if (a.start == b.start && a.end == b.end) { if (a.start == b.start && a.end == b.end) {

View File

@ -32,9 +32,9 @@ describe('diagnostics', () => {
function diagnostics(template: string): Diagnostics { function diagnostics(template: string): Diagnostics {
try { try {
mockHost.override(fileName, template); mockHost.override(fileName, template);
return ngService.getDiagnostics(fileName); return ngService.getDiagnostics(fileName) !;
} finally { } finally {
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
} }
} }
@ -84,10 +84,10 @@ describe('diagnostics', () => {
const code = '\n@Component({template: \'<div></div>\'}) export class MyComponent {}'; const code = '\n@Component({template: \'<div></div>\'}) export class MyComponent {}';
addCode(code, (fileName, content) => { addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName); const diagnostics = ngService.getDiagnostics(fileName);
const offset = content.lastIndexOf('@Component') + 1; const offset = content !.lastIndexOf('@Component') + 1;
const len = 'Component'.length; const len = 'Component'.length;
includeDiagnostic( includeDiagnostic(
diagnostics, 'Component \'MyComponent\' is not included in a module', offset, len); diagnostics !, 'Component \'MyComponent\' is not included in a module', offset, len);
}); });
}); });
@ -95,7 +95,7 @@ describe('diagnostics', () => {
const code = '\n@Component({template: \'<form></form>\'}) export class MyComponent {}'; const code = '\n@Component({template: \'<form></form>\'}) export class MyComponent {}';
addCode(code, (fileName, content) => { addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName); const diagnostics = ngService.getDiagnostics(fileName);
onlyModuleDiagnostics(diagnostics); onlyModuleDiagnostics(diagnostics !);
}); });
}); });
@ -119,7 +119,7 @@ describe('diagnostics', () => {
addCode(code, (fileName, content) => { addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName); const diagnostics = ngService.getDiagnostics(fileName);
includeDiagnostic( includeDiagnostic(
diagnostics, 'Function calls are not supported.', '() => \'foo\'', content); diagnostics !, 'Function calls are not supported.', '() => \'foo\'', content);
}); });
}); });
@ -134,7 +134,7 @@ describe('diagnostics', () => {
` @Component({template: \`<div *ngIf="something === 'foo'"></div>\`}) export class MyComponent { something: 'foo' | 'bar'; }`; ` @Component({template: \`<div *ngIf="something === 'foo'"></div>\`}) export class MyComponent { something: 'foo' | 'bar'; }`;
addCode(code, fileName => { addCode(code, fileName => {
const diagnostics = ngService.getDiagnostics(fileName); const diagnostics = ngService.getDiagnostics(fileName);
onlyModuleDiagnostics(diagnostics); onlyModuleDiagnostics(diagnostics !);
}); });
}); });
@ -144,7 +144,7 @@ describe('diagnostics', () => {
addCode(code, (fileName, content) => { addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName); const diagnostics = ngService.getDiagnostics(fileName);
includeDiagnostic( includeDiagnostic(
diagnostics, 'Unexpected callable expression. Expected a method call', 'onClick', diagnostics !, 'Unexpected callable expression. Expected a method call', 'onClick',
content); content);
}); });
}); });
@ -155,7 +155,7 @@ describe('diagnostics', () => {
` @Component({template: \`<div *ngIf="something === undefined"></div>\`}) export class MyComponent { something = 'foo'; }})`; ` @Component({template: \`<div *ngIf="something === undefined"></div>\`}) export class MyComponent { something = 'foo'; }})`;
addCode(code, fileName => { addCode(code, fileName => {
const diagnostics = ngService.getDiagnostics(fileName); const diagnostics = ngService.getDiagnostics(fileName);
onlyModuleDiagnostics(diagnostics); onlyModuleDiagnostics(diagnostics !);
}); });
}); });
@ -165,7 +165,7 @@ describe('diagnostics', () => {
` @Component({template: '<p> Using an invalid pipe {{data | dat}} </p>'}) export class MyComponent { data = 'some data'; }`; ` @Component({template: '<p> Using an invalid pipe {{data | dat}} </p>'}) export class MyComponent { data = 'some data'; }`;
addCode(code, fileName => { addCode(code, fileName => {
const diagnostic = const diagnostic =
ngService.getDiagnostics(fileName).filter(d => d.message.indexOf('pipe') > 0)[0]; ngService.getDiagnostics(fileName) !.filter(d => d.message.indexOf('pipe') > 0)[0];
expect(diagnostic).not.toBeUndefined(); expect(diagnostic).not.toBeUndefined();
expect(diagnostic.span.end - diagnostic.span.start).toBeLessThan(11); expect(diagnostic.span.end - diagnostic.span.start).toBeLessThan(11);
}); });
@ -267,7 +267,7 @@ describe('diagnostics', () => {
try { try {
cb(fileName, newContent); cb(fileName, newContent);
} finally { } finally {
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
} }
} }

View File

@ -71,7 +71,7 @@ describe('hover', () => {
function hover(code: string, hoverText: string) { function hover(code: string, hoverText: string) {
addCode(code, fileName => { addCode(code, fileName => {
let tests = 0; let tests = 0;
const markers = mockHost.getReferenceMarkers(fileName); const markers = mockHost.getReferenceMarkers(fileName) !;
const keys = Object.keys(markers.references).concat(Object.keys(markers.definitions)); const keys = Object.keys(markers.references).concat(Object.keys(markers.definitions));
for (const referenceName of keys) { for (const referenceName of keys) {
const references = (markers.references[referenceName] || const references = (markers.references[referenceName] ||
@ -96,7 +96,7 @@ describe('hover', () => {
try { try {
cb(fileName, newContent); cb(fileName, newContent);
} finally { } finally {
mockHost.override(fileName, undefined); mockHost.override(fileName, undefined !);
} }
} }

View File

@ -22,7 +22,7 @@ describe('service without angular', () => {
let ngHost = new TypeScriptServiceHost(mockHost, service); let ngHost = new TypeScriptServiceHost(mockHost, service);
let ngService = createLanguageService(ngHost); let ngService = createLanguageService(ngHost);
const fileName = '/app/test.ng'; const fileName = '/app/test.ng';
let position = mockHost.getMarkerLocations(fileName)['h1-content']; let position = mockHost.getMarkerLocations(fileName) !['h1-content'];
it('should not crash a get template references', it('should not crash a get template references',
() => expect(() => ngService.getTemplateReferences())); () => expect(() => ngService.getTemplateReferences()));

View File

@ -21,7 +21,7 @@ describe('references', () => {
let service: ts.LanguageService; let service: ts.LanguageService;
let program: ts.Program; let program: ts.Program;
let ngHost: TypeScriptServiceHost; let ngHost: TypeScriptServiceHost;
let ngService: LanguageService = createLanguageService(ngHost); let ngService: LanguageService = createLanguageService(undefined !);
beforeEach(() => { beforeEach(() => {
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh); mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);

View File

@ -63,7 +63,7 @@ missingCache.set(
missingCache.set('/node_modules/@angular/forms/src/directives/form_interface.metadata.json', true); missingCache.set('/node_modules/@angular/forms/src/directives/form_interface.metadata.json', true);
export class MockTypescriptHost implements ts.LanguageServiceHost { export class MockTypescriptHost implements ts.LanguageServiceHost {
private angularPath: string; private angularPath: string|undefined;
private nodeModulesPath: string; private nodeModulesPath: string;
private scriptVersion = new Map<string, number>(); private scriptVersion = new Map<string, number>();
private overrides = new Map<string, string>(); private overrides = new Map<string, string>();
@ -120,7 +120,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
return (this.scriptVersion.get(fileName) || 0).toString(); return (this.scriptVersion.get(fileName) || 0).toString();
} }
getScriptSnapshot(fileName: string): ts.IScriptSnapshot { getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined {
const content = this.getFileContent(fileName); const content = this.getFileContent(fileName);
if (content) return ts.ScriptSnapshot.fromString(content); if (content) return ts.ScriptSnapshot.fromString(content);
return undefined; return undefined;
@ -145,19 +145,19 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
} }
} }
getReferenceMarkers(fileName: string): ReferenceResult { getReferenceMarkers(fileName: string): ReferenceResult|undefined {
let content = this.getRawFileContent(fileName); let content = this.getRawFileContent(fileName);
if (content) { if (content) {
return getReferenceMarkers(content); return getReferenceMarkers(content);
} }
} }
getFileContent(fileName: string): string { getFileContent(fileName: string): string|undefined {
const content = this.getRawFileContent(fileName); const content = this.getRawFileContent(fileName);
if (content) return removeReferenceMarkers(removeLocationMarkers(content)); if (content) return removeReferenceMarkers(removeLocationMarkers(content));
} }
private getRawFileContent(fileName: string): string { private getRawFileContent(fileName: string): string|undefined {
if (this.overrides.has(fileName)) { if (this.overrides.has(fileName)) {
return this.overrides.get(fileName); return this.overrides.get(fileName);
} }
@ -225,7 +225,7 @@ function find(fileName: string, data: MockData): MockData|undefined {
if (typeof current === 'string') if (typeof current === 'string')
return undefined; return undefined;
else else
current = (<MockDirectory>current)[name]; current = (<MockDirectory>current)[name] !;
if (!current) return undefined; if (!current) return undefined;
} }
return current; return current;
@ -241,7 +241,7 @@ function open(fileName: string, data: MockData): string|undefined {
function directoryExists(dirname: string, data: MockData): boolean { function directoryExists(dirname: string, data: MockData): boolean {
let result = find(dirname, data); let result = find(dirname, data);
return result && typeof result !== 'string'; return !!result && typeof result !== 'string';
} }
const locationMarker = /\~\{(\w+(-\w+)*)\}/g; const locationMarker = /\~\{(\w+(-\w+)*)\}/g;

View File

@ -195,7 +195,7 @@ describe('plugin', () => {
}); });
function getMarkerLocation(fileName: string, locationMarker: string): number { function getMarkerLocation(fileName: string, locationMarker: string): number {
const location = mockHost.getMarkerLocations(fileName)[locationMarker]; const location = mockHost.getMarkerLocations(fileName) ![locationMarker];
if (location == null) { if (location == null) {
throw new Error(`No marker ${locationMarker} found.`); throw new Error(`No marker ${locationMarker} found.`);
} }

View File

@ -5,6 +5,7 @@
"stripInternal": true, "stripInternal": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"strictNullChecks": true,
"module": "es2015", "module": "es2015",
"moduleResolution": "node", "moduleResolution": "node",
"outDir": "../../dist/packages/language-service", "outDir": "../../dist/packages/language-service",

View File

@ -6,6 +6,7 @@
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"strictNullChecks": false,
"outDir": "../dist/all/@angular", "outDir": "../dist/all/@angular",
"noImplicitAny": true, "noImplicitAny": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,